/** * AI SDK UI - Custom Message Renderer * * Demonstrates: * - Markdown rendering (react-markdown) * - Code syntax highlighting (react-syntax-highlighter) * - Custom message components * - Copy code button * - Timestamp display * - User avatars * * Dependencies: * npm install react-markdown react-syntax-highlighter * npm install --save-dev @types/react-syntax-highlighter * * Usage: * 1. Install dependencies * 2. Copy this component * 3. Use in your chat */ 'use client'; import { useChat } from 'ai/react'; import { useState, FormEvent } from 'react'; import type { Message } from 'ai'; import ReactMarkdown from 'react-markdown'; import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; import { oneDark } from 'react-syntax-highlighter/dist/cjs/styles/prism'; // Custom message renderer component function MessageRenderer({ message }: { message: Message }) { const [copied, setCopied] = useState(false); const copyCode = (code: string) => { navigator.clipboard.writeText(code); setCopied(true); setTimeout(() => setCopied(false), 2000); }; return (
{/* Avatar & name */}
{message.role === 'user' ? 'U' : 'AI'}
{message.role === 'user' ? 'You' : 'Assistant'}
{/* Message content with markdown */}
{codeString}
) : ( {children} ); }, }} > {message.content}
{/* Timestamp */}
{new Date(message.createdAt || Date.now()).toLocaleTimeString()}
); } // Main chat component export default function ChatWithCustomRenderer() { const { messages, sendMessage, isLoading, error } = useChat({ api: '/api/chat', }); const [input, setInput] = useState(''); const handleSubmit = (e: FormEvent) => { e.preventDefault(); if (!input.trim()) return; sendMessage({ content: input }); setInput(''); }; return (
{/* Header */}

Custom Message Renderer

With markdown, syntax highlighting, and copy buttons

{/* Messages */}
{messages.length === 0 && (

Try asking for code examples

Messages will render with markdown and syntax highlighting

{[ 'Write a Python function to sort a list', 'Explain React hooks with code examples', 'Show me a TypeScript interface example', ].map((suggestion, idx) => ( ))}
)} {messages.map((message) => ( ))} {isLoading && (
)}
{/* Error */} {error && (
Error: {error.message}
)} {/* Input */}
setInput(e.target.value)} placeholder="Ask for code examples..." disabled={isLoading} className="flex-1 p-3 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500" />
); } // ============================================================================ // Simpler Version (without react-markdown) // ============================================================================ /* // Simple markdown parsing without external dependencies function SimpleMarkdownRenderer({ content }: { content: string }) { // Basic markdown parsing const parseMarkdown = (text: string) => { // Code blocks text = text.replace(/```(\w+)?\n([\s\S]*?)```/g, (_, lang, code) => { return `
${code}
`; }); // Inline code text = text.replace(/`([^`]+)`/g, '$1'); // Bold text = text.replace(/\*\*([^*]+)\*\*/g, '$1'); // Italic text = text.replace(/\*([^*]+)\*/g, '$1'); // Line breaks text = text.replace(/\n/g, '
'); return text; }; return (
); } */