A small generative-UI demo built for the Frontend Nation workshop "Generative UI: Building Apps That Compose Themselves."
The app asks an LLM to design a form from a natural-language prompt ("I need to hire a software engineer", "Book a dog grooming appointment", etc.). The model streams tool calls from a fixed catalog — addTextField, addEmailField, addSliderField, addSelectField, and so on — each with a Zod-validated input shape. The Vue frontend listens to the stream and renders each tool call by mapping its name to a pre-built field component.
Stack: Vite, Vue 3, Express, Vercel AI SDK, Anthropic Claude, Tailwind CSS, Zod.
You'll need Node 18+ and an Anthropic API key.
npm installcp .env.example .envOpen .env and set:
ANTHROPIC_API_KEY=sk-ant-...
npm run devThis boots the Express API on http://localhost:3001 and the Vite dev server on http://localhost:5173 (with /api proxied to the backend). Open http://localhost:5173, type a prompt, and watch the form assemble itself field-by-field as the model streams tool calls.
The demo uses Anthropic's Claude via @ai-sdk/anthropic, but the Vercel AI SDK is provider-agnostic. Swapping to OpenAI (or any other supported provider) is a small change in two files.
npm install @ai-sdk/openai
npm uninstall @ai-sdk/anthropicReplace the Anthropic import and client with OpenAI:
import { streamText } from 'ai'
import { createOpenAI } from '@ai-sdk/openai'
import { tools, systemPrompt } from './tools/index.js'
const openai = createOpenAI({ apiKey: process.env.OPENAI_API_KEY })
export async function* streamFormEvents(prompt) {
const result = streamText({
model: openai('gpt-4o-mini'),
system: systemPrompt,
prompt,
tools,
})
for await (const part of result.fullStream) {
if (part.type === 'error') {
yield ['error', { message: String(part.error) }]
continue
}
if (part.type !== 'tool-call') continue
yield [part.toolName, part.input]
}
}In .env:
OPENAI_API_KEY=sk-...
That's it. Everything else (the tool catalog, the Zod schemas, the streaming protocol, the frontend) stays the same. The AI SDK normalizes tool-calling across providers, so streamText with the same tools object works identically.
Other providers (Google, Mistral, Groq, local models via Ollama, etc.) follow the same pattern. Install the relevant @ai-sdk/* package and swap the client constructor. See the AI SDK providers docs for the full list.