Getting Started with AI and LLMs in Your Web App
Learn how to integrate large language models into your Next.js application using the Vercel AI SDK, with streaming responses and a clean API design.
AI features are no longer optional extras — users increasingly expect intelligent, context-aware experiences. The good news is that integrating LLMs into a Next.js application has never been easier, thanks to the Vercel AI SDK.
Installing the AI SDK
npm install ai @ai-sdk/openaiYou'll also need an OpenAI API key. Add it to your .env.local:
OPENAI_API_KEY=sk-...Never expose your API key on the client side. Always call the LLM from a server-side API route or Server Action.
Building a Streaming API Route
Streaming is essential for LLM responses — it shows output as it's generated rather than waiting for the full response.
// app/api/chat/route.ts
import { openai } from '@ai-sdk/openai'
import { streamText } from 'ai'
export const runtime = 'edge'
export async function POST(req: Request) {
const { messages } = await req.json()
const result = streamText({
model: openai('gpt-4o-mini'),
system: 'You are a helpful assistant for a web development blog.',
messages,
maxTokens: 1024,
})
return result.toDataStreamResponse()
}The Chat UI Component
The AI SDK provides a useChat hook that handles the entire conversation state:
'use client'
import { useChat } from 'ai/react'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import { SendHorizontal } from 'lucide-react'
export function ChatInterface() {
const { messages, input, handleInputChange, handleSubmit, isLoading } = useChat({
api: '/api/chat',
})
return (
<div className="flex flex-col h-[600px] border rounded-lg overflow-hidden">
{/* Messages */}
<div className="flex-1 overflow-y-auto p-4 space-y-4">
{messages.map((message) => (
<div
key={message.id}
className={`flex ${message.role === 'user' ? 'justify-end' : 'justify-start'}`}
>
<div
className={`max-w-[80%] rounded-lg px-4 py-2 text-sm ${
message.role === 'user'
? 'bg-primary text-primary-foreground'
: 'bg-muted text-foreground'
}`}
>
{message.content}
</div>
</div>
))}
{isLoading && (
<div className="flex justify-start">
<div className="bg-muted rounded-lg px-4 py-2 text-sm text-muted-foreground">
Thinking...
</div>
</div>
)}
</div>
{/* Input */}
<form onSubmit={handleSubmit} className="border-t p-4 flex gap-2">
<Input
value={input}
onChange={handleInputChange}
placeholder="Ask a question..."
disabled={isLoading}
className="flex-1"
/>
<Button type="submit" disabled={isLoading} size="icon">
<SendHorizontal className="h-4 w-4" />
</Button>
</form>
</div>
)
}Structured Output
Beyond chat, you can use LLMs for structured data extraction:
import { generateObject } from 'ai'
import { openai } from '@ai-sdk/openai'
import { z } from 'zod'
const { object } = await generateObject({
model: openai('gpt-4o-mini'),
schema: z.object({
title: z.string(),
tags: z.array(z.string()),
readingLevel: z.enum(['beginner', 'intermediate', 'advanced']),
}),
prompt: 'Analyse this blog post and extract metadata: ' + postContent,
})Structured output with generateObject is perfect for AI-powered content moderation, tag generation, and summarisation tasks.
Managing Costs
LLM API calls add up quickly. Here are strategies to keep costs under control:
- Use smaller models (
gpt-4o-mini) for simple tasks - Cache responses for identical prompts using Redis or
unstable_cache - Set
maxTokenslimits on all requests - Rate limit your API routes per user
The Vercel AI SDK makes it genuinely easy to add AI features to Next.js apps. Start small, measure usage, and scale from there.