6.9 KiB
Les 14: AI Chat Interface & Streaming
Hoofdstuk
Deel 4: Advanced AI & Deployment (Les 13-18)
Beschrijving
Bouwen van professionele chat interfaces met streaming responses. Message rendering, markdown support, error handling, loading states, en UX patterns voor AI-powered features.
Te Behandelen (~45 min)
- Chat UI patterns en best practices
- useChat hook deep dive (state, loading, error)
- Streaming response handling en real-time updates
- Message rendering strategies en optimizations
- Markdown rendering in chat messages
- Error handling en error boundaries
- Loading states en skeleton loaders
- User input validation and sanitization
- Accessibility in chat interfaces (ARIA labels)
- Message persistence (localStorage of database)
- Performance optimization
useChat Hook Deep Dive
State management met useChat:
'use client'
import { useChat } from 'ai/react'
export function ChatComponent() {
const {
messages, // All messages in conversation
input, // Current input text
handleInputChange, // Update input
handleSubmit, // Send message
isLoading, // Is AI responding?
error, // Any errors?
} = useChat({
api: '/api/chat',
initialMessages: [], // Optional: pre-load messages
})
return (
<>
<div>
{messages.map((msg) => (
<div key={msg.id}>
<strong>{msg.role}:</strong> {msg.content}
</div>
))}
</div>
{isLoading && <div>AI is thinking...</div>}
{error && <div>Error: {error.message}</div>}
<form onSubmit={handleSubmit}>
<input
value={input}
onChange={handleInputChange}
disabled={isLoading}
/>
<button type="submit" disabled={isLoading}>Send</button>
</form>
</>
)
}
Message Rendering Patterns
Basic pattern:
<div className="space-y-4">
{messages.map((msg) => (
<div
key={msg.id}
className={msg.role === 'user' ? 'ml-auto' : 'mr-auto'}
>
<div className="bg-gray-200 p-3 rounded">
{msg.content}
</div>
</div>
))}
</div>
With markdown rendering:
import ReactMarkdown from 'react-markdown'
<div className="bg-gray-200 p-3 rounded">
<ReactMarkdown>{msg.content}</ReactMarkdown>
</div>
With message types:
{messages.map((msg) => (
<div key={msg.id} className={msg.role === 'user' ? 'user-message' : 'ai-message'}>
{msg.content}
</div>
))}
Error Handling
Structured error handling:
try {
const response = await fetch('/api/chat', {
method: 'POST',
body: JSON.stringify({ messages }),
})
if (!response.ok) {
throw new Error(`API error: ${response.status}`)
}
// Handle streaming...
} catch (error) {
console.error('Chat error:', error)
setError({
message: 'Failed to send message',
code: error instanceof Error ? error.message : 'unknown'
})
}
Error boundary:
<ErrorBoundary fallback={<div>Chat error occurred</div>}>
<ChatComponent />
</ErrorBoundary>
Loading States
Skeleton loader:
function MessageSkeleton() {
return (
<div className="animate-pulse">
<div className="bg-gray-300 h-4 rounded w-48 mb-2" />
<div className="bg-gray-300 h-4 rounded w-64" />
</div>
)
}
{isLoading && <MessageSkeleton />}
Input Validation
Validate before sending:
function handleSubmit(e: React.FormEvent) {
e.preventDefault()
// Trim whitespace
const trimmedInput = input.trim()
// Validate non-empty
if (!trimmedInput) {
setError('Message cannot be empty')
return
}
// Validate length
if (trimmedInput.length > 1000) {
setError('Message too long (max 1000 chars)')
return
}
// Send message
handleSubmit(e)
}
Message Persistence
Save to localStorage:
const [messages, setMessages] = useState(() => {
const saved = localStorage.getItem('chat_history')
return saved ? JSON.parse(saved) : []
})
// Save whenever messages change
useEffect(() => {
localStorage.setItem('chat_history', JSON.stringify(messages))
}, [messages])
Save to database:
const saveMessage = async (message: Message) => {
await fetch('/api/messages', {
method: 'POST',
body: JSON.stringify({
content: message.content,
role: message.role,
userId: user.id,
}),
})
}
Tools
- Vercel AI SDK
- React Markdown
- Cursor
- TypeScript
Lesopdracht (2 uur, klassikaal)
Build Professional Chat Interface
Groepsdiscussie (15 min): Bespreek klassikaal de Vercel AI SDK ervaringen uit Les 13 - welke tool calling patterns werkten goed?
Deel 1: Chat Component (45 min)
Build components/ChatInterface.tsx:
- Use useChat hook
- Render messages with proper styling
- User vs AI message styling
- Input form with validation
- Tailwind + shadcn/ui components
Deel 2: Markdown & Error Handling (30 min)
- Install react-markdown:
npm install react-markdown - Render AI responses with markdown
- Add error boundary
- Show error messages to user
- Proper loading states
Deel 3: UX Improvements (30 min)
- Auto-scroll to latest message
- Disable input while loading
- Show message count/token usage
- Add clear chat history button
- Save messages to localStorage
Deel 4: Testing (15 min)
Test chat interface locally with various inputs and error scenarios.
Deliverable
- Werkende chat interface component
- Markdown rendering working
- Error handling implemented
- LocalStorage persistence
- GitHub commit with chat UI
Huiswerk (2 uur)
Integrate Chat into Your Project
Deel 1: Project Integration (1 uur)
- Add chat component to your app
- Connect to your API route with tools
- Style to match your design
- Test with actual tools/integrations
- Fix any bugs
Deel 2: Enhanced Features (30 min)
Add one of these:
- Message copy button
- Regenerate response option
- Clear history confirmation
- Export chat history
- Message timestamps
Deel 3: Performance & Polish (30 min)
- Optimize re-renders (useMemo, useCallback)
- Virtual scrolling for long chats
- Better accessibility (keyboard nav)
- Mobile responsive tweaks
- Update docs/AI-DECISIONS.md
Deliverable
- Chat fully integrated in project
- Enhanced features implemented
- Performance optimized
- GitHub commits with improvements
Leerdoelen
Na deze les kan de student:
- useChat hook volledig begrijpen en gebruiken
- Professionele chat UI patterns implementeren
- Markdown rendering in chat messages
- Error handling en error boundaries toepassen
- Loading states en skeletons bouwen
- User input valideren en sanitizen
- Message persistence (localStorage/DB)
- Accessibility in chat interfaces verbeteren
- Performance optimizations toepassen
- Complete chat feature in Next.js app integreren