mirror of
https://github.com/vercel/ai-elements.git
synced 2026-09-14 19:48:26 +08:00
cf793dd446
* Remove microfrontends * Remove /elements subpath, update URL * Fix local paths * Remove custom AI SDK navbar * Update global.css * Restore docs in path * Restore Geistdocs homepage * Split up docs, components and examples * New routes for components and examples * IA fixes * Update hero, start adding components to homepage * Experimenting with hero * Update pnpm-lock.yaml * Add new docs * Update geistdocs.tsx * Update examples, add new IDE example * Create source-code.tsx * Cleanup ide.mdx * Cleanup examples * Update homepage content * Misc fixes * Update elements-installer.tsx * Update meta.json * Deprecate Loader component for shadcn/ui Spinner * Add changeset files * Update homepage and templates * Update code-block.tsx * Misc fixes * Update callout.tsx * Remove hero badge * Resolves #328 * Build fixes * Update README.md
247 lines
6.3 KiB
Plaintext
247 lines
6.3 KiB
Plaintext
---
|
|
title: Web Preview
|
|
description: A composable component for previewing the result of a generated UI, with support for live examples and code display.
|
|
path: elements/components/web-preview
|
|
---
|
|
|
|
The `WebPreview` component provides a flexible way to showcase the result of a generated UI component, along with its source code. It is designed for documentation and demo purposes, allowing users to interact with live examples and view the underlying implementation.
|
|
|
|
<Preview path="web-preview" />
|
|
|
|
## Installation
|
|
|
|
<ElementsInstaller path="web-preview" />
|
|
|
|
## Usage with AI SDK
|
|
|
|
Build a simple v0 clone using the [v0 Platform API](https://v0.dev/docs/api/platform).
|
|
|
|
Install the `v0-sdk` package:
|
|
|
|
```package-install
|
|
npm i v0-sdk
|
|
```
|
|
|
|
Add the following component to your frontend:
|
|
|
|
```tsx title="app/page.tsx"
|
|
'use client';
|
|
|
|
import {
|
|
WebPreview,
|
|
WebPreviewBody,
|
|
WebPreviewNavigation,
|
|
WebPreviewUrl,
|
|
} from '@/components/ai-elements/web-preview';
|
|
import { useState } from 'react';
|
|
import {
|
|
Input,
|
|
PromptInputTextarea,
|
|
PromptInputSubmit,
|
|
} from '@/components/ai-elements/prompt-input';
|
|
import { Spinner } from '@/components/ui/spinner';
|
|
|
|
const WebPreviewDemo = () => {
|
|
const [previewUrl, setPreviewUrl] = useState('');
|
|
const [prompt, setPrompt] = useState('');
|
|
const [isGenerating, setIsGenerating] = useState(false);
|
|
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
if (!prompt.trim()) return;
|
|
setPrompt('');
|
|
|
|
setIsGenerating(true);
|
|
try {
|
|
const response = await fetch('/api/v0', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ prompt }),
|
|
});
|
|
|
|
const data = await response.json();
|
|
setPreviewUrl(data.demo || '/');
|
|
console.log('Generation finished:', data);
|
|
} catch (error) {
|
|
console.error('Generation failed:', error);
|
|
} finally {
|
|
setIsGenerating(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="max-w-4xl mx-auto p-6 relative size-full rounded-lg border h-[600px]">
|
|
<div className="flex flex-col h-full">
|
|
<div className="flex-1 mb-4">
|
|
{isGenerating ? (
|
|
<div className="flex flex-col items-center justify-center h-full">
|
|
<Spinner />
|
|
<p className="mt-4 text-muted-foreground">
|
|
Generating app, this may take a few seconds...
|
|
</p>
|
|
</div>
|
|
) : previewUrl ? (
|
|
<WebPreview defaultUrl={previewUrl}>
|
|
<WebPreviewNavigation>
|
|
<WebPreviewUrl />
|
|
</WebPreviewNavigation>
|
|
<WebPreviewBody src={previewUrl} />
|
|
</WebPreview>
|
|
) : (
|
|
<div className="flex items-center justify-center h-full text-muted-foreground">
|
|
Your generated app will appear here
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
<Input
|
|
onSubmit={handleSubmit}
|
|
className="w-full max-w-2xl mx-auto relative"
|
|
>
|
|
<PromptInputTextarea
|
|
value={prompt}
|
|
placeholder="Describe the app you want to build..."
|
|
onChange={(e) => setPrompt(e.currentTarget.value)}
|
|
className="pr-12 min-h-[60px]"
|
|
/>
|
|
<PromptInputSubmit
|
|
status={isGenerating ? 'streaming' : 'ready'}
|
|
disabled={!prompt.trim()}
|
|
className="absolute bottom-1 right-1"
|
|
/>
|
|
</Input>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default WebPreviewDemo;
|
|
```
|
|
|
|
Add the following route to your backend:
|
|
|
|
```ts title="app/api/v0/route.ts"
|
|
import { v0 } from 'v0-sdk';
|
|
|
|
export async function POST(req: Request) {
|
|
const { prompt }: { prompt: string } = await req.json();
|
|
|
|
const result = await v0.chats.create({
|
|
system: 'You are an expert coder',
|
|
message: prompt,
|
|
modelConfiguration: {
|
|
modelId: 'v0-1.5-sm',
|
|
imageGenerations: false,
|
|
thinking: false,
|
|
},
|
|
});
|
|
|
|
return Response.json({
|
|
demo: result.demo,
|
|
webUrl: result.webUrl,
|
|
});
|
|
}
|
|
```
|
|
|
|
## Features
|
|
|
|
- Live preview of UI components
|
|
- Composable architecture with dedicated sub-components
|
|
- Responsive design modes (Desktop, Tablet, Mobile)
|
|
- Navigation controls with back/forward functionality
|
|
- URL input and example selector
|
|
- Full screen mode support
|
|
- Console logging with timestamps
|
|
- Context-based state management
|
|
- Consistent styling with the design system
|
|
- Easy integration into documentation pages
|
|
|
|
## Props
|
|
|
|
### `<WebPreview />`
|
|
|
|
<TypeTable
|
|
type={{
|
|
defaultUrl: {
|
|
description: 'The initial URL to load in the preview.',
|
|
type: 'string',
|
|
default: '""',
|
|
},
|
|
onUrlChange: {
|
|
description: 'Callback fired when the URL changes.',
|
|
type: '(url: string) => void',
|
|
},
|
|
'...props': {
|
|
description: 'Any other props are spread to the root div.',
|
|
type: 'React.HTMLAttributes<HTMLDivElement>',
|
|
},
|
|
}}
|
|
/>
|
|
|
|
### `<WebPreviewNavigation />`
|
|
|
|
<TypeTable
|
|
type={{
|
|
'...props': {
|
|
description: 'Any other props are spread to the navigation container.',
|
|
type: 'React.HTMLAttributes<HTMLDivElement>',
|
|
},
|
|
}}
|
|
/>
|
|
|
|
### `<WebPreviewNavigationButton />`
|
|
|
|
<TypeTable
|
|
type={{
|
|
tooltip: {
|
|
description: 'Tooltip text to display on hover.',
|
|
type: 'string',
|
|
},
|
|
'...props': {
|
|
description: 'Any other props are spread to the underlying shadcn/ui Button component.',
|
|
type: 'React.ComponentProps<typeof Button>',
|
|
},
|
|
}}
|
|
/>
|
|
|
|
### `<WebPreviewUrl />`
|
|
|
|
<TypeTable
|
|
type={{
|
|
'...props': {
|
|
description: 'Any other props are spread to the underlying shadcn/ui Input component.',
|
|
type: 'React.ComponentProps<typeof Input>',
|
|
},
|
|
}}
|
|
/>
|
|
|
|
### `<WebPreviewBody />`
|
|
|
|
<TypeTable
|
|
type={{
|
|
loading: {
|
|
description: 'Optional loading indicator to display over the preview.',
|
|
type: 'React.ReactNode',
|
|
},
|
|
'...props': {
|
|
description: 'Any other props are spread to the underlying iframe.',
|
|
type: 'React.IframeHTMLAttributes<HTMLIFrameElement>',
|
|
},
|
|
}}
|
|
/>
|
|
|
|
### `<WebPreviewConsole />`
|
|
|
|
<TypeTable
|
|
type={{
|
|
logs: {
|
|
description: 'Console log entries to display in the console panel.',
|
|
type: 'Array<{ level: "log" | "warn" | "error"; message: string; timestamp: Date }>',
|
|
},
|
|
'...props': {
|
|
description: 'Any other props are spread to the root div.',
|
|
type: 'React.HTMLAttributes<HTMLDivElement>',
|
|
},
|
|
}}
|
|
/>
|