UI Generation: Letting AI Build Your Front End

What if you could ask an AI to build a user interface, and it just... did? Not just spitting out code, but generating a live, interactive component right inside your application. This is the power of AI-powered UI generation, and the Vercel AI SDK makes it surprisingly achievable.

By combining the reasoning power of LLMs with the component-based structure of modern front-end frameworks like React, we can create applications that build themselves in response to user requests.

How Does it Work?

The magic behind this is a function called generateUI. It works by letting the LLM generate a JSON object that conforms to a specific schema you define. This JSON object doesn't contain code, but rather the data and component names needed to render a UI. Your application then takes this structured data and maps it to your actual React components.

This approach is powerful because:

  • It's Secure: The AI never writes code, it only provides data, eliminating the risk of injection attacks.
  • It's Consistent: You remain in full control of your design system. The AI uses your components, ensuring the look and feel is always on-brand.
  • It's Interactive: The generated UI can include forms and buttons that trigger further AI actions, creating a continuous, interactive loop.

Building a Weather App Generator

Let's build a simple app that lets a user ask for a weather forecast, and in response, the AI will generate a weather card component.

1. Define the Component Schemas with Zod

First, we need to tell the AI what our components look like and what data they need. We use the zod library to define schemas for our UI components.

// lib/ui-generation/schemas.ts
import { z } from 'zod';

export const weatherSchema = z.object({
  city: z.string().describe("The city for the weather forecast."),
  temperature: z.number().describe("The current temperature in Celsius."),
  condition: z.enum(['Sunny', 'Cloudy', 'Rainy', 'Stormy', 'Windy'])
    .describe("The current weather condition."),
  humidity: z.number().describe("The current humidity percentage."),
});

We also define a schema for a loading spinner, so the AI can show a loading state while it "thinks."

// lib/ui-generation/schemas.ts (continued)
export const loadingSchema = z.object({
  message: z.string().describe("The message to display while loading."),
});

2. Create the Server Action

Next, we create a server action that will be called from our front end. This is where the generateUI function lives. It takes the user's prompt and our component schemas and returns a rendered UI component.

This example will use a mock weather API, but you could easily swap it out for a real one.

// app/actions.ts
'useserver'; // Or your framework's equivalent for a server action

import { generateUI } from 'ai/rsc';
import { openai } from '@ai-sdk/openai';
import { z } from 'zod';
import { weatherSchema, loadingSchema } from '@/lib/ui-generation/schemas';
import { WeatherCard } from '@/components/WeatherCard';
import { Spinner } from '@/components/Spinner';

// Mock function to simulate fetching weather data
async function getWeatherData(city: string) {
  // In a real app, you'd call a real weather API here
  console.log(`Fetching weather for ${city}...`);
  return {
    temperature: Math.floor(Math.random() * 30),
    condition: ['Sunny', 'Cloudy', 'Rainy', 'Stormy', 'Windy'][Math.floor(Math.random() * 5)],
    humidity: Math.floor(Math.random() * 100),
  };
}

export async function getWeather(prompt: string) {
  return generateUI({
    model: openai('gpt-4o'),
    system: "You are a helpful weather assistant. Use the user's prompt to determine the city for the weather forecast. Use the `getWeather` tool to fetch the data, and then display it using the `weather` component.",
    prompt,
    tools: {
      getWeather: {
        description: 'Get the weather for a city.',
        parameters: z.object({ city: z.string() }),
        generate: async function* ({ city }) {
          // Show a spinner while we fetch the data
          yield <Spinner message={`Getting the forecast for ${city}...`} />;

          const weatherData = await getWeatherData(city);

          // Return the final weather card
          return <WeatherCard {...weatherData} city={city} />;
        },
      },
    },
  });
}

Note: This example uses ai/rsc, which is designed for React Server Components. The concepts can be adapted to other frameworks.

3. Build the Front End

Finally, we create the front-end components. We need the WeatherCard and Spinner that the AI will render, and a simple form to take the user's input.

// components/WeatherCard.tsx
export function WeatherCard({ city, temperature, condition, humidity }) {
  return (
    <div className="p-4 bg-blue-100 border border-blue-300 rounded-lg">
      <h3 className="text-xl font-bold">{city}</h3>
      <p className="text-4xl">{temperature}°C</p>
      <p>Condition: {condition}</p>
      <p>Humidity: {humidity}%</p>
    </div>
  );
}

// components/Spinner.tsx
export function Spinner({ message }) {
  return (
    <div className="p-4 bg-gray-100 border border-gray-300 rounded-lg">
      <p>{message}</p>
    </div>
  );
}

And the main chat interface:

// app/page.tsx
'use client';

import { useState } from 'react';
import { getWeather } from './actions';

export default function Page() {
  const [prompt, setPrompt] = useState('');
  const [ui, setUi] = useState(null);

  const handleSubmit = async (e) => {
    e.preventDefault();
    const newUi = await getWeather(prompt);
    setUi(newUi);
  };

  return (
    <div className="p-4">
      <form onSubmit={handleSubmit} className="flex gap-2">
        <input
          type="text"
          value={prompt}
          onChange={(e) => setPrompt(e.target.value)}
          placeholder="e.g., What's the weather in London?"
          className="flex-1 p-2 border rounded-lg"
        />
        <button type="submit" className="px-4 py-2 bg-blue-600 text-white rounded-lg">
          Ask
        </button>
      </form>
      <div className="mt-4">{ui}</div>
    </div>
  );
}

Now, when a user types a city and clicks "Ask", the AI will first generate the Spinner component, then call our tool to get the data, and finally replace the spinner with the fully rendered WeatherCard.

UI generation represents a significant leap forward, turning conversation into creation and blurring the lines between the user, the developer, and the AI.