Initial commit
This commit is contained in:
251
docs/components/web-preview.md
Normal file
251
docs/components/web-preview.md
Normal file
@@ -0,0 +1,251 @@
|
||||
# Web Preview
|
||||
|
||||
URL: /components/web-preview
|
||||
|
||||
---
|
||||
|
||||
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
|
||||
|
||||
```tsx
|
||||
import { WebPreview, WebPreviewNavigation, WebPreviewUrl, WebPreviewBody } from "@/components/ai-elements/web-preview";
|
||||
```
|
||||
|
||||
```tsx
|
||||
<WebPreview defaultUrl="https://ai-sdk.dev" style={{ height: "400px" }}>
|
||||
<WebPreviewNavigation>
|
||||
<WebPreviewUrl src="https://ai-sdk.dev" />
|
||||
</WebPreviewNavigation>
|
||||
<WebPreviewBody src="https://ai-sdk.dev" />
|
||||
</WebPreview>
|
||||
```
|
||||
|
||||
## 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 { Loader } from "../ai-elements/loader";
|
||||
|
||||
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">
|
||||
<Loader />
|
||||
<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>',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
Reference in New Issue
Block a user