> ## Documentation Index
> Fetch the complete documentation index at: https://docs.morphllm.com/llms.txt
> Use this file to discover all available pages before exploring further.

# VibeArtifact

> Production-ready component display with action buttons

# VibeArtifact

VibeArtifact combines the Artifact container from AI Elements SDK with VibeFrame to create a complete component display experience. It includes Run, Copy, Regenerate, Download, Share actions and a Code/Preview toggle—everything users expect when interacting with generated components.

## Basic Usage

```tsx theme={null}
import { VibeArtifact } from 'genkit/react'

function ComponentPreview({ component }) {
  return (
    <VibeArtifact
      name={component.name}
      description={component.description}
      code={component.code}
      data={appData}
    />
  )
}
```

This renders:

```
┌─────────────────────────────────────────────────────────────┐
│  Sales Dashboard                                            │
│  Pipeline visualization · Updated 2 minutes ago             │
│                                        ▶ 📋 🔄 ⬇ 📤 │ 👁/< >│
├─────────────────────────────────────────────────────────────┤
│                                                             │
│                   [Component Preview]                       │
│                                                             │
└─────────────────────────────────────────────────────────────┘
```

## Props

| Prop           | Type                      | Default      | Description                        |
| -------------- | ------------------------- | ------------ | ---------------------------------- |
| `name`         | `string`                  | **required** | Component name displayed in header |
| `code`         | `string`                  | **required** | React component code               |
| `description`  | `string`                  | -            | Optional description               |
| `data`         | `Record<string, unknown>` | `{}`         | Data passed to the component       |
| `updatedAt`    | `Date`                    | -            | Timestamp for "Updated X ago"      |
| `defaultView`  | `'preview' \| 'code'`     | `'preview'`  | Initial view mode                  |
| `className`    | `string`                  | -            | CSS class for container            |
| `minHeight`    | `number`                  | `150`        | Minimum preview height             |
| `maxHeight`    | `number`                  | `500`        | Maximum preview height             |
| `onRun`        | `() => void`              | -            | Called when Run is clicked         |
| `onRegenerate` | `() => void`              | -            | Called when Regenerate is clicked  |
| `onShare`      | `() => void`              | -            | Called when Share is clicked       |

## Action Buttons

### Built-in Actions

| Button           | Icon    | Behavior                                               |
| ---------------- | ------- | ------------------------------------------------------ |
| **Run**          | ▶       | Refreshes the iframe, re-executes component            |
| **Copy**         | 📋      | Copies code to clipboard, shows checkmark              |
| **Regenerate**   | 🔄      | Calls `onRegenerate` callback (only shown if provided) |
| **Download**     | ⬇       | Downloads code as `{name}.tsx` file                    |
| **Share**        | 📤      | Calls `onShare` callback (only shown if provided)      |
| **Code/Preview** | 👁/\< > | Toggles between preview and code view                  |

### Handling Actions

```tsx theme={null}
function StudioPreview({ component, prompt }) {
  const [isRegenerating, setIsRegenerating] = useState(false)

  const handleRegenerate = async () => {
    setIsRegenerating(true)
    const newComponent = await generateComponent(prompt)
    setComponent(newComponent)
    setIsRegenerating(false)
  }

  const handleShare = async () => {
    const shareUrl = await createShareLink(component.id)
    await navigator.clipboard.writeText(shareUrl)
    toast.success('Share link copied!')
  }

  return (
    <VibeArtifact
      name={component.name}
      description={component.description}
      code={component.code}
      data={appData}
      updatedAt={component.updatedAt}
      onRegenerate={handleRegenerate}
      onShare={handleShare}
    />
  )
}
```

## View Modes

### Preview Mode (Default)

Shows the rendered component in a VibeFrame:

```tsx theme={null}
<VibeArtifact
  name="Stats Card"
  code={code}
  data={data}
  defaultView="preview"
/>
```

### Code Mode

Shows the raw code with syntax highlighting:

```tsx theme={null}
<VibeArtifact
  name="Stats Card"
  code={code}
  data={data}
  defaultView="code"
/>
```

Users can toggle between modes using the Code/Preview button.

## Timestamps

Show when the component was last updated:

```tsx theme={null}
<VibeArtifact
  name="Dashboard Widget"
  code={code}
  updatedAt={new Date('2025-01-05T10:30:00')}
/>

// Displays: "Updated 2 minutes ago"
```

The timestamp automatically formats as:

* "just now" (\< 1 minute)
* "X minutes ago" (\< 1 hour)
* "X hours ago" (\< 1 day)
* "X days ago" (\< 1 week)
* Full date (> 1 week)

## Composition with Artifact Primitives

VibeArtifact is built on the Artifact primitives. For custom layouts, use them directly:

```tsx theme={null}
import {
  Artifact,
  ArtifactHeader,
  ArtifactTitle,
  ArtifactDescription,
  ArtifactActions,
  ArtifactAction,
  ArtifactContent,
} from 'genkit/react'
import { VibeFrame } from 'genkit/react'

function CustomArtifact({ component }) {
  return (
    <Artifact>
      <ArtifactHeader>
        <div>
          <ArtifactTitle>{component.name}</ArtifactTitle>
          <ArtifactDescription>
            By {component.author}
          </ArtifactDescription>
        </div>
        <ArtifactActions>
          <ArtifactAction
            icon={Star}
            tooltip="Favorite"
            onClick={() => favorite(component.id)}
          />
          <ArtifactAction
            icon={Trash}
            tooltip="Delete"
            onClick={() => deleteComponent(component.id)}
          />
        </ArtifactActions>
      </ArtifactHeader>
      <ArtifactContent className="p-0">
        <VibeFrame code={component.code} data={data} />
      </ArtifactContent>
    </Artifact>
  )
}
```

## Artifact Primitives

### Artifact

Main container with border and shadow:

```tsx theme={null}
<Artifact className="w-full">
  {/* children */}
</Artifact>
```

### ArtifactHeader

Header with title and actions:

```tsx theme={null}
<ArtifactHeader>
  <div>
    <ArtifactTitle>Component Name</ArtifactTitle>
    <ArtifactDescription>Description</ArtifactDescription>
  </div>
  <ArtifactActions>
    {/* action buttons */}
  </ArtifactActions>
</ArtifactHeader>
```

### ArtifactAction

Action button with optional tooltip:

```tsx theme={null}
<ArtifactAction
  icon={Copy}
  tooltip="Copy to clipboard"
  onClick={() => copyCode()}
/>

// With custom content
<ArtifactAction tooltip="Custom action">
  <MyIcon />
</ArtifactAction>
```

### ArtifactContent

Scrollable content area:

```tsx theme={null}
<ArtifactContent className="p-0">
  <VibeFrame code={code} />
</ArtifactContent>
```

### ArtifactClose

Close button (for modal/panel usage):

```tsx theme={null}
<ArtifactClose onClick={() => setOpen(false)} />
```

## Styling

VibeArtifact uses Tailwind CSS and respects your theme:

```tsx theme={null}
// Light mode
<VibeArtifact className="bg-white" ... />

// Dark mode (automatic with dark: classes)
<VibeArtifact className="dark:bg-gray-900" ... />

// Custom width
<VibeArtifact className="max-w-2xl mx-auto" ... />
```

## Example: Full Studio Integration

```tsx theme={null}
'use client'

import { useState } from 'react'
import { VibeArtifact } from 'genkit/react'

interface Component {
  id: string
  name: string
  description: string
  code: string
  updatedAt: Date
}

export function VibeStudio() {
  const [component, setComponent] = useState<Component | null>(null)
  const [prompt, setPrompt] = useState('')

  const handleGenerate = async () => {
    const response = await fetch('/api/vibe/generate', {
      method: 'POST',
      body: JSON.stringify({ prompt })
    })
    const data = await response.json()
    setComponent({
      ...data.component,
      updatedAt: new Date()
    })
  }

  const handleRegenerate = () => {
    handleGenerate()
  }

  const handleSave = async () => {
    await fetch('/api/vibe/save', {
      method: 'POST',
      body: JSON.stringify(component)
    })
    toast.success('Saved to library!')
  }

  const handleShare = async () => {
    const url = `${window.location.origin}/shared/${component.id}`
    await navigator.clipboard.writeText(url)
    toast.success('Share link copied!')
  }

  return (
    <div className="grid grid-cols-2 h-screen">
      {/* Chat Panel */}
      <div className="border-r p-4">
        <input
          value={prompt}
          onChange={(e) => setPrompt(e.target.value)}
          placeholder="Describe your component..."
          className="w-full p-3 border rounded"
        />
        <button onClick={handleGenerate}>
          Generate
        </button>
      </div>

      {/* Preview Panel */}
      <div className="p-6 bg-muted/30">
        {component ? (
          <>
            <VibeArtifact
              name={component.name}
              description={component.description}
              code={component.code}
              data={sampleData}
              updatedAt={component.updatedAt}
              onRegenerate={handleRegenerate}
              onShare={handleShare}
            />
            <button
              onClick={handleSave}
              className="mt-4 w-full"
            >
              Save to Library
            </button>
          </>
        ) : (
          <EmptyState />
        )}
      </div>
    </div>
  )
}
```

## Next Steps

<CardGroup cols={2}>
  <Card title="VibeFrame" icon="window" href="/sdk/genkit/vibe-frame">
    Learn about the secure rendering engine
  </Card>

  <Card title="Storage" icon="database" href="/sdk/genkit/storage">
    Persist components to your database
  </Card>
</CardGroup>
