Skip to content

unlearndev/formy

Repository files navigation

Formy

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.

Getting started

You'll need Node 18+ and an Anthropic API key.

1. Install dependencies

npm install

2. Copy the example env file

cp .env.example .env

3. Add your API key

Open .env and set:

ANTHROPIC_API_KEY=sk-ant-...

4. Start the dev server

npm run dev

This 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.

Using a different model provider

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.

1. Install the provider package

npm install @ai-sdk/openai
npm uninstall @ai-sdk/anthropic

2. Update server/generate.js

Replace 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]
  }
}

3. Update env vars

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.

About

A small generative-UI demo built for the Frontend Nation workshop "Generative UI: Building Apps That Compose Themselves."

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors