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:
- Code Artifacts - Interactive code editing with syntax highlighting
- 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