A streaming chatbot UI built with React + Vite + TypeScript, backed by a Vercel Function that proxies requests to the Anthropic API. The API key never leaves the server.
- Streaming responses — tokens render in real time as Claude generates them
- Loading states — animated typing indicator while waiting for the first token; send button becomes a stop button mid-stream
- Error handling — network and API errors surface in a dismissible banner; partial responses are preserved if the user stops early
- Backend proxy —
ANTHROPIC_API_KEYlives only in the Edge Function environment, never in client code - Light + dark mode — follows system preference automatically
chatbot/
├── api/
│ └── chat.ts # Vercel Function — Anthropic API proxy
├── src/
│ ├── types.ts # Shared TypeScript types (Message, Role)
│ ├── hooks/
│ │ └── useChat.ts # Streaming state management hook
│ ├── components/
│ │ ├── MessageList.tsx # Scrolling message list + typing dots
│ │ └── ChatInput.tsx # Auto-resize textarea, send / stop button
│ ├── App.tsx # Root shell: header, error banner, layout
│ ├── App.css # Chat UI styles
│ ├── index.css # Global CSS variables + reset
│ └── main.tsx # React entry point
├── .env.example # Required environment variables (template)
├── .env.local # Local secrets — git-ignored
├── tests/ # Unit and browser e2e tests
└── vercel.json # Vercel project config
- Node.js 18+
- Vercel CLI — needed to run the Edge Function locally
npm i -g vercel@latest
- An Anthropic API key
npm installFill in your API key in .env.local:
ANTHROPIC_API_KEY=sk-ant-...
.env.localis git-ignored (*.localrule in.gitignore) — it will never be committed.
Use Vercel Dev so the Edge Function in api/ is served alongside the Vite dev server:
npm run dev:vercelOpen http://localhost:3000.
Why not
npm run dev?
Plainvitedoesn't run theapi/functions.npm run dev:vercelstarts Vercel Dev, which runs Vite internally and mounts the Edge Functions on/api/*, matching the production environment exactly.
Run the unit tests:
npm run testRun the browser e2e test:
npm run test:e2eThe e2e test starts Vercel Dev and mocks /api/chat, so it verifies the chat flow without calling Anthropic.
Live app: https://chatbot-delta-smoky-90.vercel.app/
# Link this directory to a Vercel project
vercel link
# Add the secret to Vercel (or use the Dashboard)
vercel env add ANTHROPIC_API_KEY
# → paste your key, select the environments (Production / Preview / Development)# Preview deployment
vercel
# Production deployment
vercel --prodVercel auto-detects the Vite framework, builds the frontend, and deploys api/chat.ts as an Edge Function.
Browser Vercel Edge Function Anthropic API
│ │ │
│──── POST /api/chat ───────────▶│ │
│ { messages: [...] } │──── messages.create() ────▶│
│ │ stream: true │
│ │◀─── content_block_delta ───│
│◀─── text/plain stream ─────────│ (token by token) │
│ (chunks arrive in real time) │ │
- The frontend sends the full conversation history to
/api/chat. - The Edge Function opens a streaming request to Anthropic using
@anthropic-ai/sdk. - Each
text_deltaevent is forwarded to the browser via aReadableStream. useChatreads chunks withresponse.body.getReader()and appends them to the assistant message in React state — triggering a re-render per chunk.
Open api/chat.ts and update the MODEL constant at the top:
const MODEL = 'claude-opus-4-5'; // ← change thisSee the Anthropic models overview for available model IDs.
| Layer | Technology |
|---|---|
| Frontend framework | React 19 |
| Build tool | Vite |
| Language | TypeScript |
| Hosting + functions | Vercel |
| AI provider | Anthropic (@anthropic-ai/sdk) |