@@ -40,10 +40,9 @@ npm i v0-sdk
4040
4141Add the following component to your frontend:
4242<!-- TOOD: Using Vue 3 example -->
43- ``` tsx title="app/page.tsx"
44- ' use client'
45-
46- import { useState } from ' react'
43+ ``` vue title="app.vue"
44+ <script setup lang="ts">
45+ import { Loader } from '@/components/ai-elements/loader'
4746import {
4847 Input,
4948 PromptInputSubmit,
@@ -55,102 +54,92 @@ import {
5554 WebPreviewNavigation,
5655 WebPreviewUrl,
5756} from '@/components/ai-elements/web-preview'
58- import { Loader } from ' ../ai-elements/loader'
59-
60- function WebPreviewDemo() {
61- const [previewUrl, setPreviewUrl] = useState (' ' )
62- const [prompt, setPrompt] = useState (' ' )
63- const [isGenerating, setIsGenerating] = useState (false )
64-
65- const handleSubmit = async (e : React .FormEvent ) => {
66- e .preventDefault ()
67- if (! prompt .trim ())
68- return
69- setPrompt (' ' )
70-
71- setIsGenerating (true )
72- try {
73- const response = await fetch (' /api/v0' , {
74- method: ' POST' ,
75- headers: { ' Content-Type' : ' application/json' },
76- body: JSON .stringify ({ prompt }),
77- })
78-
79- const data = await response .json ()
80- setPreviewUrl (data .demo || ' /' )
81- console .log (' Generation finished:' , data )
82- }
83- catch (error ) {
84- console .error (' Generation failed:' , error )
85- }
86- finally {
87- setIsGenerating (false )
88- }
89- }
9057
91- return (
92- <div className = " max-w-4xl mx-auto p-6 relative size-full rounded-lg border h-[600px]" >
93- <div className = " flex flex-col h-full" >
94- <div className = " flex-1 mb-4" >
95- { isGenerating
96- ? (
97- <div className = " flex flex-col items-center justify-center h-full" >
98- <Loader />
99- <p className = " mt-4 text-muted-foreground" >
100- Generating app, this may take a few seconds...
101- </p >
102- </div >
103- )
104- : previewUrl
105- ? (
106- <WebPreview defaultUrl = { previewUrl } >
107- <WebPreviewNavigation >
108- <WebPreviewUrl />
109- </WebPreviewNavigation >
110- <WebPreviewBody src = { previewUrl } />
111- </WebPreview >
112- )
113- : (
114- <div className = " flex items-center justify-center h-full text-muted-foreground" >
115- Your generated app will appear here
116- </div >
117- )}
58+ const previewUrl = ref('')
59+ const prompt = ref('')
60+ const isGenerating = ref(false)
61+
62+ async function handleSubmit(e: Event) {
63+ e.preventDefault()
64+ if (!prompt.value.trim())
65+ return
66+ prompt.value = ''
67+
68+ isGenerating.value = true
69+ try {
70+ const response = await fetch('/api/v0', {
71+ method: 'POST',
72+ headers: { 'Content-Type': 'application/json' },
73+ body: JSON.stringify({ prompt: prompt.value }),
74+ })
75+
76+ const data = await response.json()
77+ previewUrl.value = data.demo || '/'
78+ console.log('Generation finished:', data)
79+ }
80+ catch (error) {
81+ console.error('Generation failed:', error)
82+ }
83+ finally {
84+ isGenerating.value = false
85+ }
86+ }
87+ </script>
88+
89+ <template>
90+ <div class="max-w-4xl mx-auto p-6 relative size-full rounded-lg border h-[600px]">
91+ <div class="flex flex-col h-full">
92+ <div class="flex-1 mb-4">
93+ <div v-if="isGenerating" class="flex flex-col items-center justify-center h-full">
94+ <Loader />
95+ <p v-if="isGenerating" class="mt-4 text-muted-foreground">
96+ Generating app, this may take a few seconds...
97+ </p>
11898 </div>
99+ <WebPreview v-else-if="previewUrl" :default-url="previewUrl">
100+ <WebPreviewNavigation>
101+ <WebPreviewUrl />
102+ </WebPreviewNavigation>
103+ <WebPreviewBody :src="previewUrl" />
104+ </WebPreview>
105+ <div v-else class="flex items-center justify-center h-full text-muted-foreground">
106+ Your generated app will appear here
107+ </div>
108+ </div>
119109
120- <Input
121- onSubmit = { handleSubmit }
122- className = " w-full max-w-2xl mx-auto relative"
110+ <Input
111+ class="w-full max-w-2xl mx-auto relative"
112+ @submit="handleSubmit"
113+ >
114+ <PromptInputTextarea
115+ :value="prompt"
116+ placeholder="Describe the app you want to build..."
117+ class="pr-12 min-h-[60px]"
118+ @change="(e: any) => (prompt = e?.target?.value ?? '')"
123119 >
124- <PromptInputTextarea
125- value = { prompt }
126- placeholder = " Describe the app you want to build..."
127- onChange = { e => setPrompt (e .currentTarget .value )}
128- className = " pr-12 min-h-[60px]"
129- />
130120 <PromptInputSubmit
131- status = { isGenerating ? ' streaming' : ' ready' }
132- disabled = { ! prompt .trim ()}
133- className = " absolute bottom-1 right-1"
121+ : status=" isGenerating ? 'streaming' : 'ready'"
122+ : disabled=" !prompt.trim()"
123+ class ="absolute bottom-1 right-1"
134124 />
135- </Input >
136- </div >
125+ </PromptInputTextarea >
126+ </Input >
137127 </div>
138- )
139- }
140-
141- export default WebPreviewDemo
128+ </div>
129+ </template>
142130```
143131
144132Add the following route to your backend:
145133
146134<!-- TOOD: Using Nuxt example -->
147135
148- ``` ts title="app/api/v0/route.ts"
136+ ``` ts title="server/api/v0.post.ts"
137+ import type { ChatsCreateResponse } from ' v0-sdk'
138+ import { defineEventHandler , readBody } from ' h3'
149139import { v0 } from ' v0-sdk'
150140
151- export async function POST(req : Request ) {
152- const { prompt }: { prompt: string } = await req .json ()
153-
141+ export default defineEventHandler (async (event ) => {
142+ const { prompt }: { prompt: string } = await readBody (event )
154143 const result = await v0 .chats .create ({
155144 system: ' You are an expert coder' ,
156145 message: prompt ,
@@ -159,13 +148,13 @@ export async function POST(req: Request) {
159148 imageGenerations: false ,
160149 thinking: false ,
161150 },
162- })
151+ }) as ChatsCreateResponse
163152
164- return Response . json ( {
153+ return {
165154 demo: result .demo ,
166155 webUrl: result .webUrl ,
167- })
168- }
156+ }
157+ })
169158```
170159
171160## Features
0 commit comments