Logo

Artifacts

Interactive code and document editing with version management

Artifacts are interactive, editable content that appears in the chat canvas. They allow users to view, edit, and interact with generated code and documents directly within the chat interface, similar to OpenAI's ChatGPT canvas feature.

Artifact System Overview

The artifact system consists of:

  • ChatCanvas - Side panel for displaying artifacts
  • Artifact Viewers - Components for rendering different artifact types
  • Version Management - Track changes and revisions
  • Edit Capabilities - In-place editing with syntax highlighting

Artifact Types

The system supports two main artifact types:

  1. Code Artifacts - Interactive code editing with syntax highlighting
  2. Document Artifacts - Rich text document editing with markdown support

Code Artifacts

Code artifacts provide interactive code editing with full syntax highlighting and language support.

Creating Code Artifacts

// Server-side: Create code artifact annotation
const codeArtifact = {
  type: 'artifact',
  data: {
    type: 'code',
    data: {
      title: 'Data Visualization Script',
      file_name: 'visualize_data.py',
      language: 'python',
      code: `
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np

def create_sales_chart(data_file):
    """Create a sales visualization chart."""
    # Load the data
    df = pd.read_csv(data_file)

    # Prepare the data
    monthly_sales = df.groupby('month')['revenue'].sum()

    # Create the visualization
    plt.figure(figsize=(12, 6))
    bars = plt.bar(monthly_sales.index, monthly_sales.values,
                   color='steelblue', alpha=0.8)

    # Customize the chart
    plt.title('Monthly Sales Revenue', fontsize=16, fontweight='bold')
    plt.xlabel('Month', fontsize=12)
    plt.ylabel('Revenue ($)', fontsize=12)
    plt.xticks(rotation=45)

    # Add value labels on bars
    for bar in bars:
        height = bar.get_height()
        plt.text(bar.get_x() + bar.get_width()/2., height,
                f'${height:,.0f}', ha='center', va='bottom')

    plt.tight_layout()
    plt.grid(True, alpha=0.3)
    plt.show()

    return monthly_sales

# Example usage
if __name__ == "__main__":
    sales_data = create_sales_chart('sales_data.csv')
    print(f"Total annual revenue: ${sales_data.sum():,.2f}")
      `
    }
  }
}

Streaming Code Artifacts

// In your API route
export async function POST(request: Request) {
  const { messages } = await request.json()

  const stream = new ReadableStream({
    async start(controller) {
      const encoder = new TextEncoder()

      // Send initial response
      controller.enqueue(
        encoder.encode(
          '0:"I\'ll create a Python script for data visualization:\\n"\\n'
        )
      )

      // Generate code (could be from LLM)
      const generatedCode = await generateCodeWithLLM(messages)

      // Send code artifact
      const artifact = {
        type: 'artifact',
        data: {
          type: 'code',
          data: {
            title: 'Data Visualization Script',
            file_name: 'chart_generator.py',
            language: 'python',
            code: generatedCode,
          },
        },
      }

      // wrap the annotation in a code block with the language key is 'annotation'
      const codeBlock = `\n\`\`\`annotation\n${JSON.stringify(codeArtifact)}\n\`\`\`\n`

      // send the artifact with the 0: prefix to make it inline
      controller.enqueue(encoder.encode(`0:${JSON.stringify(codeBlock)}\\n`))

      // Send follow-up text
      controller.enqueue(
        encoder.encode(
          '0:"You can edit this code directly in the canvas. Would you like me to explain any part of it?"\\n'
        )
      )

      controller.close()
    },
  })

  return new Response(stream, {
    headers: {
      'Content-Type': 'text/plain; charset=utf-8',
      'X-Vercel-AI-Data-Stream': 'v1',
    },
  })
}

Supported Languages

The code artifact system supports syntax highlighting for:

  • JavaScript/TypeScript - .js, .ts, .jsx, .tsx
  • Python - .py, .pyx
  • CSS/SCSS - .css, .scss, .sass
  • HTML - .html, .htm
  • JSON - .json
  • Markdown - .md, .mdx
  • SQL - .sql
  • Shell/Bash - .sh, .bash

Code Artifact Features

// The canvas automatically provides these features:
// - Syntax highlighting
// - Line numbers
// - Code folding
// - Search and replace
// - Auto-indentation
// - Bracket matching
// - Error highlighting (for supported languages)

Document Artifacts

Document artifacts provide rich text editing with markdown support and real-time preview.

Creating Document Artifacts

const documentArtifact = {
  type: 'artifact',
  data: {
    type: 'document',
    data: {
      title: 'API Integration Guide',
      content: `
# API Integration Guide

## Overview

This guide walks you through integrating our REST API into your application.

## Authentication

All API requests require authentication using API keys:

\`\`\`bash
curl -H "Authorization: Bearer YOUR_API_KEY" \\
     https://api.example.com/v1/users
\`\`\`

## Endpoints

### User Management

#### Get User Profile

\`\`\`
GET /v1/users/{userId}
\`\`\`

**Response:**
\`\`\`json
{
  "id": "user_123",
  "name": "John Doe",
  "email": "john@example.com",
  "created_at": "2024-01-15T10:30:00Z"
}
\`\`\`

#### Update User Profile

\`\`\`
PUT /v1/users/{userId}
Content-Type: application/json

{
  "name": "John Smith",
  "email": "john.smith@example.com"
}
\`\`\`

### Data Operations

#### List Records

\`\`\`
GET /v1/records?limit=20&offset=0
\`\`\`

**Query Parameters:**
- \`limit\`: Number of records to return (default: 20, max: 100)
- \`offset\`: Number of records to skip (default: 0)
- \`sort\`: Sort field (default: created_at)
- \`order\`: Sort order (asc/desc, default: desc)

## Error Handling

The API uses standard HTTP status codes:

| Code | Description |
|------|-------------|
| 200  | Success |
| 400  | Bad Request |
| 401  | Unauthorized |
| 404  | Not Found |
| 500  | Internal Server Error |

**Error Response Format:**
\`\`\`json
{
  "error": {
    "code": "INVALID_REQUEST",
    "message": "The request is missing required parameters",
    "details": {
      "missing_fields": ["name", "email"]
    }
  }
}
\`\`\`

## Rate Limiting

- **Free Tier**: 100 requests per hour
- **Pro Tier**: 1,000 requests per hour
- **Enterprise**: Custom limits

Rate limit headers are included in all responses:

\`\`\`
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 1640995200
\`\`\`

## SDKs and Libraries

We provide official SDKs for popular languages:

### JavaScript/Node.js

\`\`\`bash
npm install @example/api-client
\`\`\`

\`\`\`javascript
import { ApiClient } from '@example/api-client'

const client = new ApiClient({
  apiKey: 'your-api-key'
})

const user = await client.users.get('user_123')
console.log(user)
\`\`\`

### Python

\`\`\`bash
pip install example-api-client
\`\`\`

\`\`\`python
from example_api import ApiClient

client = ApiClient(api_key='your-api-key')
user = client.users.get('user_123')
print(user)
\`\`\`

## Best Practices

1. **Always use HTTPS** for API requests
2. **Store API keys securely** - never commit them to version control
3. **Implement retry logic** with exponential backoff
4. **Cache responses** when appropriate to reduce API calls
5. **Monitor rate limits** and implement proper handling

## Webhooks

Configure webhooks to receive real-time notifications:

\`\`\`json
{
  "url": "https://yourapp.com/webhooks/api",
  "events": ["user.created", "user.updated", "user.deleted"],
  "secret": "webhook-secret-key"
}
\`\`\`

## Support

- **Documentation**: https://docs.example.com
- **Support Email**: support@example.com
- **Status Page**: https://status.example.com

Happy coding! 🚀
      `,
    },
  },
}

Document Features

Document artifacts provide:

  • Rich Text Editing - WYSIWYG editor with markdown output
  • Live Preview - Real-time rendering of markdown content
  • Formatting Tools - Bold, italic, headers, lists, links
  • Code Blocks - Syntax-highlighted code snippets
  • Tables - Table creation and editing
  • Math Support - LaTeX math rendering
  • Image Embedding - Image insertion and management

Canvas Integration

Basic Canvas Setup

import { ChatSection, ChatCanvas } from '@llamaindex/chat-ui'

function ChatWithCanvas() {
  const handler = useChat({ api: '/api/chat' })

  return (
    <ChatSection handler={handler} className="flex h-full">
      <div className="flex-1">
        <ChatMessages />
        <ChatInput />
      </div>
      <ChatCanvas className="w-1/2 border-l" />
    </ChatSection>
  )
}

Canvas Auto-Show

The canvas automatically appears when artifacts are present:

// Canvas appears automatically when message contains artifacts
<ChatMessage message={messageWithArtifact}>
  <ChatMessage.Content>
    <ChatMessage.Content.Markdown />
  </ChatMessage.Content>
</ChatMessage>

Version Management

Artifacts support version tracking and restoration:

Version History

import { useChatCanvas } from '@llamaindex/chat-ui'

function ArtifactVersions() {
  const { currentArtifact, updateArtifact } = useChatCanvas()

  if (!currentArtifact) return null

  const versions = currentArtifact.versions || []

  const restoreVersion = (version: any) => {
    updateArtifact({
      ...currentArtifact,
      content: version.content,
      versions: [
        ...versions,
        {
          id: generateId(),
          content: currentArtifact.content,
          timestamp: new Date().toISOString(),
          description: 'Current version backup',
        },
      ],
    })
  }

  return (
    <div className="space-y-2">
      <h3 className="font-semibold">Version History</h3>
      {versions.map((version, index) => (
        <div key={version.id} className="flex items-center justify-between">
          <div>
            <p className="text-sm font-medium">
              Version {versions.length - index}
            </p>
            <p className="text-xs text-gray-500">
              {new Date(version.timestamp).toLocaleString()}
            </p>
          </div>
          <button
            onClick={() => restoreVersion(version)}
            className="text-sm text-blue-600 hover:underline"
          >
            Restore
          </button>
        </div>
      ))}
    </div>
  )
}

Save Changes

function SaveArtifactChanges() {
  const { currentArtifact, updateArtifact } = useChatCanvas()
  const [hasChanges, setHasChanges] = useState(false)

  const saveChanges = () => {
    if (!currentArtifact || !hasChanges) return

    // Create version backup
    const newVersion = {
      id: generateId(),
      content: currentArtifact.originalContent,
      timestamp: new Date().toISOString(),
      description: 'Auto-saved version',
    }

    updateArtifact({
      ...currentArtifact,
      versions: [...(currentArtifact.versions || []), newVersion],
      originalContent: currentArtifact.content,
    })

    setHasChanges(false)
  }

  return (
    <button
      onClick={saveChanges}
      disabled={!hasChanges}
      className="rounded bg-blue-600 px-3 py-1 text-white disabled:opacity-50"
    >
      {hasChanges ? 'Save Changes' : 'Saved'}
    </button>
  )
}

Artifact Actions

Export Functionality

function ArtifactExport() {
  const { currentArtifact } = useChatCanvas()

  const exportAsFile = () => {
    if (!currentArtifact) return

    const content =
      currentArtifact.type === 'code'
        ? currentArtifact.code
        : currentArtifact.content

    const filename =
      currentArtifact.type === 'code'
        ? currentArtifact.file_name
        : `${currentArtifact.title}.md`

    const blob = new Blob([content], {
      type: 'text/plain;charset=utf-8',
    })

    const url = URL.createObjectURL(blob)
    const link = document.createElement('a')
    link.href = url
    link.download = filename
    link.click()

    URL.revokeObjectURL(url)
  }

  return (
    <button
      onClick={exportAsFile}
      className="flex items-center gap-2 rounded border px-3 py-1 text-sm"
    >
      <DownloadIcon className="h-4 w-4" />
      Export
    </button>
  )
}

Copy to Clipboard

import { useCopyToClipboard } from '@llamaindex/chat-ui'

function CopyArtifact() {
  const { currentArtifact } = useChatCanvas()
  const { copyToClipboard, isCopied } = useCopyToClipboard()

  const handleCopy = () => {
    if (!currentArtifact) return

    const content =
      currentArtifact.type === 'code'
        ? currentArtifact.code
        : currentArtifact.content

    copyToClipboard(content)
  }

  return (
    <button
      onClick={handleCopy}
      className="flex items-center gap-2 rounded border px-3 py-1 text-sm"
    >
      <CopyIcon className="h-4 w-4" />
      {isCopied ? 'Copied!' : 'Copy'}
    </button>
  )
}

Next Steps

  • Examples - See complete artifact implementations
  • Customization - Style and customize artifact appearance
  • Widgets - Explore related widget functionality
  • Annotations - Understand the annotation system