Skip to main content

Overview

The Web Artifacts Builder skill provides a complete toolkit for creating sophisticated, multi-component HTML artifacts for Claude.ai. It uses modern frontend technologies including React 18, TypeScript, Vite, Tailwind CSS, and shadcn/ui components.
Use for complex artifacts requiring state management, routing, or shadcn/ui components. For simple single-file HTML/JSX artifacts, this skill is unnecessary—create them directly.

When to Use This Skill

Use the Web Artifacts Builder skill when creating artifacts that need:
  • Multiple React components with state management
  • Sophisticated UI with shadcn/ui component library
  • Client-side routing or navigation
  • Complex interactions and data flow
  • Professional styling with Tailwind CSS
  • TypeScript for type safety

Technology Stack

  • React 18 - UI component library
  • TypeScript - Type-safe development
  • Vite - Fast build tooling
  • Parcel - Bundling to single HTML file
  • Tailwind CSS - Utility-first styling
  • shadcn/ui - High-quality React components (40+ pre-installed)

Workflow

1

Initialize Project

Run the initialization script to create a new React project
2

Develop Artifact

Edit the generated code to build your artifact
3

Bundle to Single HTML

Use the bundling script to create a self-contained HTML file
4

Display to User

Share the bundled artifact in the Claude conversation
5

Test (Optional)

Optionally test the artifact if requested or if issues arise

Design & Style Guidelines

VERY IMPORTANT: To avoid “AI slop” aesthetics, avoid:
  • Excessive centered layouts
  • Purple gradients
  • Uniform rounded corners
  • Inter font overuse
Strive for unique, contextually appropriate designs.

Quick Start Guide

Step 1: Initialize Project

Run the initialization script to create a fully configured React project:
bash scripts/init-artifact.sh <project-name>
cd <project-name>
What gets created:
  • ✅ React + TypeScript (via Vite)
  • ✅ Tailwind CSS 3.4.1 with shadcn/ui theming system
  • ✅ Path aliases (@/) configured for clean imports
  • ✅ 40+ shadcn/ui components pre-installed
  • ✅ All Radix UI dependencies included
  • ✅ Parcel configured for bundling (via .parcelrc)
  • ✅ Node 18+ compatibility (auto-detects and pins Vite version)

Step 2: Develop Your Artifact

Edit the generated files to build your artifact. The initialization script creates a standard React project structure:
project-name/
├── src/
│   ├── components/     # Your React components
│   ├── lib/           # Utilities and helpers
│   ├── App.tsx        # Main application component
│   └── main.tsx       # Entry point
├── index.html         # HTML template
├── package.json       # Dependencies
├── tailwind.config.js # Tailwind configuration
└── tsconfig.json      # TypeScript configuration

Step 3: Bundle to Single HTML File

Bundle your React app into a self-contained HTML artifact:
bash scripts/bundle-artifact.sh
Output: bundle.html - A single HTML file with all JavaScript, CSS, and dependencies inlined.
  1. Installs bundling dependencies:
    • parcel
    • @parcel/config-default
    • parcel-resolver-tspaths
    • html-inline
  2. Creates .parcelrc config with path alias support
  3. Builds with Parcel (no source maps for smaller size)
  4. Inlines all assets into single HTML using html-inline
Requirements:
  • Your project must have an index.html in the root directory
  • All imports must be valid and resolvable

Step 4: Share Artifact with User

Share the bundle.html file in the conversation with the user. Claude.ai will render it as an interactive artifact.

Step 5: Testing/Visualizing (Optional)

Testing is completely optional. Only perform if necessary or requested. Avoid testing upfront as it adds latency. Test later, after presenting the artifact, if issues arise.
To test or visualize the artifact, use available tools:
  • Playwright for automated browser testing
  • Other skills like webapp-testing
  • Built-in Puppeteer capabilities

Common Development Tasks

Using shadcn/ui Components

The project comes with 40+ shadcn/ui components pre-installed. Import and use them:
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"

export function LoginForm() {
  return (
    <Card className="w-full max-w-md">
      <CardHeader>
        <CardTitle>Login</CardTitle>
        <CardDescription>Enter your credentials</CardDescription>
      </CardHeader>
      <CardContent>
        <div className="space-y-4">
          <div>
            <Label htmlFor="email">Email</Label>
            <Input id="email" type="email" placeholder="[email protected]" />
          </div>
          <div>
            <Label htmlFor="password">Password</Label>
            <Input id="password" type="password" />
          </div>
          <Button className="w-full">Sign In</Button>
        </div>
      </CardContent>
    </Card>
  )
}

State Management

import { useState } from 'react'
import { Button } from "@/components/ui/button"

export function Counter() {
  const [count, setCount] = useState(0)
  
  return (
    <div className="space-y-4">
      <p className="text-2xl font-bold">Count: {count}</p>
      <div className="space-x-2">
        <Button onClick={() => setCount(count + 1)}>Increment</Button>
        <Button onClick={() => setCount(count - 1)} variant="outline">
          Decrement
        </Button>
        <Button onClick={() => setCount(0)} variant="ghost">
          Reset
        </Button>
      </div>
    </div>
  )
}

Using Path Aliases

The @/ alias points to the src directory for cleaner imports:
// Instead of:
import { Button } from '../../../components/ui/button'

// Use:
import { Button } from '@/components/ui/button'

// For your own utilities:
import { formatDate } from '@/lib/utils'
import { UserCard } from '@/components/UserCard'

Styling with Tailwind CSS

export function Hero() {
  return (
    <div className="bg-gradient-to-r from-blue-500 to-purple-600 text-white">
      <div className="container mx-auto px-4 py-16">
        <h1 className="text-4xl md:text-6xl font-bold mb-4">
          Welcome to Our Platform
        </h1>
        <p className="text-xl md:text-2xl mb-8 text-blue-100">
          Build something amazing today
        </p>
        <Button size="lg" variant="secondary">
          Get Started
        </Button>
      </div>
    </div>
  )
}

Available shadcn/ui Components

The initialization script pre-installs these components:
  • Accordion
  • Alert & Alert Dialog
  • Avatar
  • Badge
  • Breadcrumb
  • Button
  • Calendar
  • Card
  • Carousel
  • Checkbox
  • Collapsible
  • Command
  • Context Menu
  • Dialog
  • Dropdown Menu
  • Form
  • Hover Card
  • Input
  • Input OTP
  • Label
  • Menubar
  • Navigation Menu
  • Pagination
  • Popover
  • Progress
  • Radio Group
  • Resizable
  • Scroll Area
  • Select
  • Separator
  • Sheet
  • Skeleton
  • Slider
  • Sonner (toast notifications)
  • Switch
  • Table
  • Tabs
  • Textarea
  • Toast
  • Toggle & Toggle Group
  • Tooltip
All components include their required Radix UI dependencies.

Example Artifacts

Dashboard with Charts

import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { Progress } from "@/components/ui/progress"

export function Dashboard() {
  return (
    <div className="p-8">
      <h1 className="text-3xl font-bold mb-8">Analytics Dashboard</h1>
      
      <Tabs defaultValue="overview" className="space-y-4">
        <TabsList>
          <TabsTrigger value="overview">Overview</TabsTrigger>
          <TabsTrigger value="analytics">Analytics</TabsTrigger>
          <TabsTrigger value="reports">Reports</TabsTrigger>
        </TabsList>
        
        <TabsContent value="overview" className="space-y-4">
          <div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
            <Card>
              <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
                <CardTitle className="text-sm font-medium">
                  Total Revenue
                </CardTitle>
              </CardHeader>
              <CardContent>
                <div className="text-2xl font-bold">$45,231.89</div>
                <p className="text-xs text-muted-foreground">
                  +20.1% from last month
                </p>
                <Progress value={70} className="mt-2" />
              </CardContent>
            </Card>
            {/* More cards... */}
          </div>
        </TabsContent>
      </Tabs>
    </div>
  )
}

Form with Validation

import { useState } from 'react'
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { Textarea } from "@/components/ui/textarea"
import { useToast } from "@/components/ui/use-toast"

export function ContactForm() {
  const { toast } = useToast()
  const [formData, setFormData] = useState({
    name: '',
    email: '',
    message: ''
  })
  
  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault()
    toast({
      title: "Message sent!",
      description: "We'll get back to you soon.",
    })
    setFormData({ name: '', email: '', message: '' })
  }
  
  return (
    <form onSubmit={handleSubmit} className="space-y-4">
      <div>
        <Label htmlFor="name">Name</Label>
        <Input
          id="name"
          value={formData.name}
          onChange={(e) => setFormData({ ...formData, name: e.target.value })}
          required
        />
      </div>
      <div>
        <Label htmlFor="email">Email</Label>
        <Input
          id="email"
          type="email"
          value={formData.email}
          onChange={(e) => setFormData({ ...formData, email: e.target.value })}
          required
        />
      </div>
      <div>
        <Label htmlFor="message">Message</Label>
        <Textarea
          id="message"
          value={formData.message}
          onChange={(e) => setFormData({ ...formData, message: e.target.value })}
          required
        />
      </div>
      <Button type="submit">Send Message</Button>
    </form>
  )
}

Troubleshooting

The bundled HTML file can become large with many dependencies. To reduce size:
  • Remove unused shadcn/ui components from your code
  • Avoid importing entire icon libraries; import only needed icons
  • Minimize the use of large data files embedded in the bundle
  • Consider code splitting if the artifact supports it
Common build errors and solutions:TypeScript errors:
  • Ensure all imports are typed correctly
  • Check that path aliases are used correctly (@/ instead of relative paths)
  • Verify all dependencies are installed
Parcel errors:
  • Ensure index.html exists in the root directory
  • Check that all file paths in imports are correct
  • Verify .parcelrc was created by the bundle script
If Tailwind styles aren’t working:
  • Ensure tailwind.config.js includes all content paths
  • Verify that Tailwind directives are in your CSS file
  • Check that class names are spelled correctly
  • Confirm shadcn/ui theme configuration is intact

Reference

shadcn/ui Documentation:
https://ui.shadcn.com/docs/components
Visit the official documentation for:
  • Component API references
  • Theming and customization
  • Accessibility guidelines
  • Additional component examples

Best Practices

Key Recommendations:
  1. Component Organization - Keep components small and focused
  2. Type Safety - Leverage TypeScript for better DX and fewer bugs
  3. Accessibility - Use shadcn/ui components which include ARIA attributes
  4. Responsive Design - Use Tailwind’s responsive utilities (sm:, md:, lg:)
  5. Testing - Only test after creating the artifact, not before
  6. Avoid AI Slop - Create unique designs, not generic purple gradient dashboards

Build docs developers (and LLMs) love