Production-grade chat widget for any Next.js site. Drop-in component, streaming responses, prompt caching, and proper error handling.
Powered by: Claude (Anthropic) · Next.js 14 App Router · TypeScript · Tailwind CSS
Most "chat widget" boilerplates skip the hard parts — streaming, typed error handling, mobile responsive UX, and abort support. This repo ships the full thing in a small, readable codebase.
- Real-time streaming — Tokens render as they arrive via
ReadableStream. Sub-second time-to-first-token. - Prompt caching ready —
cache_control: ephemeralset on the system prompt; caches automatically once context grows past Opus 4.7's 4096-token minimum. - Typed error handling —
Anthropic.RateLimitError,AuthenticationError, andAPIErrorinstances are caught withinstanceofchecks (never string-matching) and surfaced as friendly UI messages. - Mobile responsive — Full-screen on phones, floating panel on desktop.
- Markdown rendering — Code blocks, lists, links rendered via
react-markdownwithremark-gfm. - Abort support — Users can stop a streaming response mid-generation.
- Conversation history capping — Last 20 turns sent to API to control token cost.
- Input sanitization — 8K-char limit per message, role validation, JSON parse guard.
npm install
cp .env.example .env.local
# Edit .env.local — paste your ANTHROPIC_API_KEY
npm run devOpen http://localhost:3000 and click the chat bubble.
Get an API key at console.anthropic.com/settings/keys.
The live demo is deployed with two commands:
npm install -g vercel
vercel --prodWhen prompted, add ANTHROPIC_API_KEY as an environment variable (set it as sensitive).
ai-chat-widget/
├── app/
│ ├── api/chat/route.ts # Streaming endpoint (Anthropic SDK → ReadableStream)
│ ├── globals.css # Tailwind + markdown styles
│ ├── layout.tsx
│ └── page.tsx # Landing page with embedded widget
├── components/
│ ├── chat-widget.tsx # Main widget (launcher + panel + stream reader)
│ ├── chat-input.tsx # Auto-resizing textarea with send/stop
│ └── message-bubble.tsx # User/assistant bubbles + typing indicator
├── lib/
│ ├── anthropic.ts # SDK client singleton
│ ├── types.ts # ChatMessage, UIMessage types
│ └── utils.ts # cn() helper
├── docs/
│ └── screenshot.png # README image
├── .env.example
└── package.json
- Client
POST /api/chatwith the full message history. - API route validates input, instantiates
Anthropicclient, callsclient.messages.stream({...}). - Server iterates the SDK's async iterator, writing each
text_deltato aReadableStream<Uint8Array>. - Client reads the stream with
getReader(), decoding chunks viaTextDecoderand updating React state on each chunk. - On completion, the controller closes and the connection ends.
The clean separation between server-side token streaming and client-side incremental UI update keeps the code under 600 lines total.
The API route catches typed Anthropic exceptions in a try/catch block and writes a markdown-formatted error message into the stream. Errors appear in the assistant bubble as italicized text:
_Rate limited. Please wait a moment and try again._
Network errors (no response) are caught client-side in chat-widget.tsx and shown the same way.
Edit SYSTEM_PROMPT in app/api/chat/route.ts. To make prompt caching effective, expand it past 4096 tokens (e.g., paste your product docs, FAQ, or knowledge base).
model: 'claude-opus-4-7', // most capable (default)
// or:
model: 'claude-sonnet-4-6', // 3x cheaper, faster
// or:
model: 'claude-haiku-4-5', // 5x cheaper, fastestEdit tailwind.config.ts → theme.extend.colors.brand. The widget uses brand-600 for primary, brand-700 for hover.
Copy the components/ and lib/ folders into your existing Next.js project. Add <ChatWidget /> to any page. The API route at /api/chat works with any Next.js 14 App Router setup.
Per 1,000 messages (assuming ~500 input tokens / ~200 output tokens):
| Model | Cost / 1K msgs | Notes |
|---|---|---|
| Opus 4.7 | ~$3.50 | Most capable |
| Sonnet 4.6 | ~$2.10 | Best balance |
| Haiku 4.5 | ~$0.70 | Cheapest |
With prompt caching (system prompt > 4K tokens), cached reads cost ~0.1× the input rate.
MIT — use for any purpose, commercial or personal.
@leninug — full-stack developer focused on React, TypeScript, and AI integrations. Available for freelance work on Fiverr.
