The Vercel AI SDK makes it incredibly easy to add AI capabilities to React applications. In this tutorial, we'll build several AI-powered components from scratch, including a streaming chatbot and intelligent content generator.
Getting Started
First, install the necessary packages:
npm install ai @ai-sdk/openai @ai-sdk/anthropic
npm install @types/react @types/node # if using TypeScriptSet up your environment variables:
# .env.local
OPENAI_API_KEY=your_openai_key
ANTHROPIC_API_KEY=your_anthropic_keyBasic Chat Component
Let's start with a simple chat interface that streams responses:
// components/ChatBot.jsx
import { useChat } from 'ai/react';
import { useState } from 'react';
export default function ChatBot() {
const { messages, input, handleInputChange, handleSubmit, isLoading } = useChat({
api: '/api/chat',
});
return (
<div className="chat-container">
<div className="messages">
{messages.map((message) => (
<div key={message.id} className={`message ${message.role}`}>
<strong>{message.role === 'user' ? 'You' : 'AI'}:</strong>
<p>{message.content}</p>
</div>
))}
{isLoading && <div className="loading">AI is thinking...</div>}
</div>
<form onSubmit={handleSubmit} className="input-form">
<input
value={input}
onChange={handleInputChange}
placeholder="Ask me anything..."
disabled={isLoading}
/>
<button type="submit" disabled={isLoading}>
Send
</button>
</form>
</div>
);
}API Route Setup
// pages/api/chat.js (or app/api/chat/route.js for App Router)
import { openai } from '@ai-sdk/openai';
import { streamText } from 'ai';
export async function POST(req) {
const { messages } = await req.json();
const result = await streamText({
model: openai('gpt-4-turbo'),
messages,
system: 'You are a helpful assistant that provides concise, accurate answers.',
});
return result.toAIStreamResponse();
}useChat Hook Features:
Built-in State Management
- Automatic message history
- Loading states
- Error handling
- Input management
Streaming Support
- Real-time response streaming
- Partial message updates
- Automatic UI updates
Customization Options
- Custom API endpoints
- Message preprocessing
- Response formatting
- Error callbacks
Advanced Content Generator
Let's build a more sophisticated component that generates blog content:
// components/ContentGenerator.jsx
import { useCompletion } from 'ai/react';
import { useState } from 'react';
export default function ContentGenerator() {
const [topic, setTopic] = useState('');
const [contentType, setContentType] = useState('blog-post');
const {
completion,
input,
handleInputChange,
handleSubmit,
isLoading,
error,
} = useCompletion({
api: '/api/generate',
body: {
contentType,
topic,
},
});
const handleGenerate = (e) => {
e.preventDefault();
handleSubmit(e, {
body: {
prompt: input,
contentType,
topic,
},
});
};
return (
<div className="content-generator">
<div className="controls">
<input
type="text"
placeholder="Enter topic..."
value={topic}
onChange={(e) => setTopic(e.target.value)}
/>
<select
value={contentType}
onChange={(e) => setContentType(e.target.value)}
>
<option value="blog-post">Blog Post</option>
<option value="social-media">Social Media</option>
<option value="email">Email</option>
<option value="product-description">Product Description</option>
</select>
<form onSubmit={handleGenerate}>
<textarea
value={input}
onChange={handleInputChange}
placeholder="Additional instructions..."
rows={3}
/>
<button type="submit" disabled={isLoading || !topic}>
{isLoading ? 'Generating...' : 'Generate Content'}
</button>
</form>
</div>
{error && (
<div className="error">
Error: {error.message}
</div>
)}
{completion && (
<div className="generated-content">
<h3>Generated Content:</h3>
<div className="content-preview">
{completion.split('\n').map((paragraph, index) => (
<p key={index}>{paragraph}</p>
))}
</div>
<button onClick={() => navigator.clipboard.writeText(completion)}>
Copy to Clipboard
</button>
</div>
)}
</div>
);
}API Route for Content Generation
// pages/api/generate.js
import { openai } from '@ai-sdk/openai';
import { streamText } from 'ai';
const contentPrompts = {
'blog-post': 'Write a comprehensive blog post about',
'social-media': 'Create engaging social media content about',
'email': 'Write a professional email about',
'product-description': 'Create a compelling product description for',
};
export async function POST(req) {
const { prompt, contentType, topic } = await req.json();
const systemPrompt = `${contentPrompts[contentType]} ${topic}. ${prompt || ''}
Guidelines:
- Keep the tone professional but engaging
- Include relevant keywords naturally
- Structure content with clear headings
- Make it actionable and valuable`;
const result = await streamText({
model: openai('gpt-4-turbo'),
prompt: systemPrompt,
temperature: 0.7,
maxTokens: 1000,
});
return result.toAIStreamResponse();
}
Comments