/** * AI SDK UI - Streaming Structured Data * * Demonstrates: * - useObject hook for streaming structured data * - Partial object updates (live as schema fields fill in) * - Zod schema validation * - Loading states * - Error handling * * Use cases: * - Forms generation * - Recipe creation * - Product specs * - Structured content generation * * Usage: * 1. Copy this component * 2. Create /api/object route with streamObject * 3. Define Zod schema matching your needs */ 'use client'; import { useObject } from 'ai/react'; import { z } from 'zod'; import { FormEvent, useState } from 'react'; // Define the schema for the object const recipeSchema = z.object({ recipe: z.object({ name: z.string().describe('Recipe name'), description: z.string().describe('Short description'), prepTime: z.number().describe('Preparation time in minutes'), cookTime: z.number().describe('Cooking time in minutes'), servings: z.number().describe('Number of servings'), difficulty: z.enum(['easy', 'medium', 'hard']), ingredients: z.array( z.object({ item: z.string(), amount: z.string(), }) ), instructions: z.array(z.string()), }), }); export default function ObjectStreaming() { const { object, submit, isLoading, error, stop } = useObject({ api: '/api/recipe', schema: recipeSchema, }); const [input, setInput] = useState(''); const handleSubmit = (e: FormEvent) => { e.preventDefault(); if (!input.trim()) return; submit(input); setInput(''); }; return (
{/* Header */}

AI Recipe Generator

Streaming structured data with live updates

{/* Input form */}
setInput(e.target.value)} placeholder="e.g., 'chocolate chip cookies' or 'thai green curry'" disabled={isLoading} className="w-full p-3 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:bg-gray-100" />
{isLoading ? ( ) : ( )}
{/* Error */} {error && (
Error: {error.message}
)} {/* Generated recipe */} {(object?.recipe || isLoading) && (
{/* Recipe header */}

{object?.recipe?.name || ( {isLoading ? 'Generating name...' : 'Recipe name'} )}

{object?.recipe?.description && (

{object.recipe.description}

)}
{/* Recipe meta */}
Prep Time
{object?.recipe?.prepTime ? ( `${object.recipe.prepTime} min` ) : ( ... )}
Cook Time
{object?.recipe?.cookTime ? ( `${object.recipe.cookTime} min` ) : ( ... )}
Servings
{object?.recipe?.servings || ( ... )}
Difficulty
{object?.recipe?.difficulty || ( ... )}
{/* Ingredients */}

Ingredients

{object?.recipe?.ingredients && object.recipe.ingredients.length > 0 ? (
    {object.recipe.ingredients.map((ingredient, idx) => (
  • {ingredient.amount} {ingredient.item}
  • ))}
) : (

{isLoading ? 'Loading ingredients...' : 'No ingredients yet'}

)}
{/* Instructions */}

Instructions

{object?.recipe?.instructions && object.recipe.instructions.length > 0 ? (
    {object.recipe.instructions.map((step, idx) => (
  1. {idx + 1} {step}
  2. ))}
) : (

{isLoading ? 'Loading instructions...' : 'No instructions yet'}

)}
{/* Loading indicator */} {isLoading && (
Generating recipe...
)}
)} {/* Example prompts */} {!object && !isLoading && (

Try these:

{[ 'Chocolate chip cookies', 'Thai green curry', 'Classic margarita pizza', 'Banana bread', ].map((example, idx) => ( ))}
)}
); }