Files
novi-lessons/Samenvattingen/Les14-Samenvatting.md
2026-03-11 14:07:00 +01:00

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:

  1. Use useChat hook
  2. Render messages with proper styling
  3. User vs AI message styling
  4. Input form with validation
  5. Tailwind + shadcn/ui components

Deel 2: Markdown & Error Handling (30 min)

  1. Install react-markdown: npm install react-markdown
  2. Render AI responses with markdown
  3. Add error boundary
  4. Show error messages to user
  5. Proper loading states

Deel 3: UX Improvements (30 min)

  1. Auto-scroll to latest message
  2. Disable input while loading
  3. Show message count/token usage
  4. Add clear chat history button
  5. 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)

  1. Add chat component to your app
  2. Connect to your API route with tools
  3. Style to match your design
  4. Test with actual tools/integrations
  5. 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)

  1. Optimize re-renders (useMemo, useCallback)
  2. Virtual scrolling for long chats
  3. Better accessibility (keyboard nav)
  4. Mobile responsive tweaks
  5. 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