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.
VibeFrame
VibeFrame renders AI-generated React components in a sandboxed iframe. It handles code compilation, data passing, auto-resizing, and error handling—all while maintaining strict security isolation.
Basic Usage
import { VibeFrame } from 'genkit/react'
function Preview() {
const code = `
export default function Widget({ data }) {
return (
<div className="p-4 bg-blue-50 rounded-lg">
<h2 className="text-xl font-bold">{data.title}</h2>
<p className="text-gray-600">{data.description}</p>
</div>
)
}
`
return (
<VibeFrame
code={code}
data={{ title: 'Hello', description: 'World' }}
/>
)
}
Props
| Prop | Type | Default | Description |
|---|
code | string | - | React component code to render |
id | string | - | Component ID (for saved components) |
data | Record<string, unknown> | {} | Data passed to the component as props |
rendererUrl | string | /api/vibe/render | Base URL for the render endpoint |
className | string | - | CSS class for the container |
minHeight | number | 100 | Minimum iframe height in pixels |
maxHeight | number | 600 | Maximum iframe height in pixels |
onRender | () => void | - | Called when component renders successfully |
onError | (error: string) => void | - | Called when rendering fails |
loadingFallback | ReactNode | - | Custom loading UI |
errorFallback | ReactNode | ((error: string) => ReactNode) | - | Custom error UI |
How It Works
1. Code Compilation
When you provide code, VibeFrame generates a data URL containing:
- React 18 (loaded from CDN)
- Babel standalone (for JSX compilation)
- Tailwind CSS (loaded from CDN)
- Your component code
// This code:
<VibeFrame code={myCode} />
// Becomes this data URL:
data:text/html;charset=utf-8,<!DOCTYPE html>...
2. PostMessage Bridge
The iframe and parent communicate via PostMessage:
Parent Iframe
│ │
│ ───── VIBE_READY ──────────> │ (iframe loaded)
│ │
│ <──── VIBE_DATA ─────────── │ (parent sends data)
│ │
│ ───── VIBE_RENDERED ───────> │ (component rendered)
│ { height: 240 } │
│ │
│ ───── VIBE_ERROR ──────────> │ (if error occurs)
│ { error: "..." } │
3. Auto-Resize
The iframe automatically resizes based on content:
// Iframe sends its height after rendering
window.parent.postMessage({
type: 'VIBE_RENDERED',
height: document.body.scrollHeight
}, '*')
VibeFrame constrains this between minHeight and maxHeight.
Security Model
Sandbox Restrictions
<iframe sandbox="allow-scripts" />
This sandbox policy:
- ✅ Allows JavaScript execution
- ❌ Blocks same-origin access (can’t read parent DOM)
- ❌ Blocks form submission
- ❌ Blocks popups and new windows
- ❌ Blocks top-level navigation
- ❌ Blocks plugins
Code Validation
Before rendering, code is validated for dangerous patterns:
// These patterns are blocked:
eval() // Dynamic execution
Function() // Function constructor
new Function() // Function constructor
import() // Dynamic imports
require() // CommonJS imports
process. // Node.js globals
global. // Node.js globals
window. // Browser globals
document. // DOM access
__proto__ // Prototype pollution
constructor[] // Constructor access
Rendering Modes
Direct Code Rendering
Pass code directly for previews:
<VibeFrame
code={generatedCode}
data={previewData}
/>
Saved Component Rendering
Pass an ID to render saved components:
<VibeFrame
id="component-123"
data={liveData}
rendererUrl="/api/vibe/render"
/>
This fetches the component from your render endpoint.
Custom Loading States
<VibeFrame
code={code}
data={data}
loadingFallback={
<div className="flex items-center gap-2">
<Spinner />
<span>Compiling component...</span>
</div>
}
errorFallback={(error) => (
<div className="text-red-500">
<p>Failed to render: {error}</p>
<button onClick={retry}>Try Again</button>
</div>
)}
/>
Event Handling
function Preview() {
const [status, setStatus] = useState<'loading' | 'ready' | 'error'>('loading')
const [error, setError] = useState<string | null>(null)
return (
<>
<VibeFrame
code={code}
data={data}
onRender={() => {
setStatus('ready')
analytics.track('component_rendered')
}}
onError={(err) => {
setStatus('error')
setError(err)
analytics.track('component_error', { error: err })
}}
/>
{status === 'ready' && <Badge>Live</Badge>}
{status === 'error' && <Alert>{error}</Alert>}
</>
)
}
Data Updates
Data is automatically sent when it changes:
function LiveDashboard() {
const [deals, setDeals] = useState([])
useEffect(() => {
const unsubscribe = subscribeToDeals(setDeals)
return unsubscribe
}, [])
// VibeFrame re-renders when deals change
return (
<VibeFrame
code={dashboardCode}
data={{ deals }}
/>
)
}
API Endpoint Setup
For saved components, create a render endpoint:
// app/api/vibe/render/[id]/route.ts
import { NextRequest } from 'next/server'
import { vibeApi } from '@/lib/vibe'
export async function GET(
request: NextRequest,
{ params }: { params: { id: string } }
) {
const component = await vibeApi.load(params.id)
if (!component) {
return new Response('Not found', { status: 404 })
}
const html = generateRenderHtml(component.code)
return new Response(html, {
headers: { 'Content-Type': 'text/html' }
})
}
Best Practices
1. Always Set Max Height
Prevent malicious components from expanding infinitely:
<VibeFrame
code={untrustedCode}
maxHeight={600}
/>
2. Validate Data Before Passing
Don’t pass sensitive data to generated components:
// ❌ Bad: Passing sensitive data
<VibeFrame data={{ user, apiKey, internalConfig }} />
// ✅ Good: Only pass display data
<VibeFrame data={{ userName: user.name, stats: publicStats }} />
3. Handle Errors Gracefully
Always provide error handling:
<VibeFrame
code={code}
onError={(error) => {
toast.error('Component failed to render')
logError(error)
}}
errorFallback={<ComponentErrorState />}
/>
4. Use Loading States
Show users something while compiling:
<VibeFrame
code={code}
loadingFallback={<Skeleton className="h-64" />}
/>