Skip to main content

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

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

PropTypeDefaultDescription
namestringrequiredComponent name displayed in header
codestringrequiredReact component code
descriptionstring-Optional description
dataRecord<string, unknown>{}Data passed to the component
updatedAtDate-Timestamp for “Updated X ago”
defaultView'preview' | 'code''preview'Initial view mode
classNamestring-CSS class for container
minHeightnumber150Minimum preview height
maxHeightnumber500Maximum 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

ButtonIconBehavior
RunRefreshes the iframe, re-executes component
Copy📋Copies code to clipboard, shows checkmark
Regenerate🔄Calls onRegenerate callback (only shown if provided)
DownloadDownloads code as {name}.tsx file
Share📤Calls onShare callback (only shown if provided)
Code/Preview👁/< >Toggles between preview and code view

Handling Actions

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:
<VibeArtifact
  name="Stats Card"
  code={code}
  data={data}
  defaultView="preview"
/>

Code Mode

Shows the raw code with syntax highlighting:
<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:
<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:
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:
<Artifact className="w-full">
  {/* children */}
</Artifact>

ArtifactHeader

Header with title and actions:
<ArtifactHeader>
  <div>
    <ArtifactTitle>Component Name</ArtifactTitle>
    <ArtifactDescription>Description</ArtifactDescription>
  </div>
  <ArtifactActions>
    {/* action buttons */}
  </ArtifactActions>
</ArtifactHeader>

ArtifactAction

Action button with optional tooltip:
<ArtifactAction
  icon={Copy}
  tooltip="Copy to clipboard"
  onClick={() => copyCode()}
/>

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

ArtifactContent

Scrollable content area:
<ArtifactContent className="p-0">
  <VibeFrame code={code} />
</ArtifactContent>

ArtifactClose

Close button (for modal/panel usage):
<ArtifactClose onClick={() => setOpen(false)} />

Styling

VibeArtifact uses Tailwind CSS and respects your theme:
// 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

'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

VibeFrame

Learn about the secure rendering engine

Storage

Persist components to your database