{"slug": "a-minimal-chatbot-template-built-with-next-js", "title": "A minimal chatbot template built with Next.js", "summary": "Vercel released a minimal chatbot template built with Next.js, the AI SDK, shadcn/ui, shadcn/react, shadcn/typeset, and the Vercel AI Gateway, featuring streaming chat with markdown rendering, tool calling, web search, and a human-in-the-loop questionnaire. The template requires no configuration on Vercel deployments, which authenticate to the AI Gateway via OIDC, and includes a public unauthenticated /api/chat route that spends AI Gateway credits, prompting developers to add rate limiting, spend caps, and auth before production use.", "body_md": "A minimal chatbot template built with Next.js, the [AI SDK](https://ai-sdk.dev), [shadcn/ui](https://ui.shadcn.com), [shadcn/react](https://ui.shadcn.com/docs/react/message-scroller), [shadcn/typeset](https://ui.shadcn.com/docs/typeset) and the [Vercel AI Gateway](https://vercel.com/docs/ai-gateway).\n\n- Streaming chat with markdown rendering and shadcn/typeset\n- Tool calling example\n- Web search via each provider's built-in search tool\n- Human-in-the-loop questionnaire. The model can ask clarifying questions, answered with the shadcn questionnaire component\n\nThat's it — no configuration needed. Vercel deployments authenticate to the AI Gateway automatically via OIDC, and usage runs on your team's [AI Gateway credits](https://vercel.com/docs/ai-gateway/pricing).\n\n```\npnpm install\n```\n\nThen give the app a gateway credential, either by pulling an OIDC token from your linked Vercel project:\n\n```\nvercel link\nvercel env pull\n```\n\nor by creating an API key in the Vercel dashboard (**AI Gateway → API Keys**) and adding it to `.env.local`\n\n:\n\n```\ncp .env.example .env.local\n# then set AI_GATEWAY_API_KEY=...\n```\n\nStart the dev server:\n\n```\npnpm dev\n```\n\n| Env var | Required | Description |\n|---|---|---|\n`AI_GATEWAY_API_KEY` |\nLocal dev only | AI Gateway API key. Not needed on Vercel deployments (OIDC). |\n\nThe model list lives in [lib/models.ts](/shadcn-ui/chatbot-template/blob/main/lib/models.ts) — the first entry is the default model.\n\nThe `/api/chat`\n\nroute is **public and unauthenticated** — every request spends your AI Gateway credits. That's fine for a personal demo, but before putting it in front of real traffic you should:\n\n**Rate limit it.** Add[Vercel Firewall / WAF](https://vercel.com/docs/security/vercel-waf)rules orso a single client can't drain your credits (denial-of-wallet).`@upstash/ratelimit`\n\n**Cap spend.** Set an[AI Gateway spend limit](https://vercel.com/docs/ai-gateway/observability-and-spend/budgets)as a backstop.**Add auth** if the chatbot isn't meant to be public.\n\nThe route already validates the request body, restricts models to [lib/models.ts](/shadcn-ui/chatbot-template/blob/main/lib/models.ts), caps output tokens and step count, and aborts generation on client disconnect — but those bound a single request, not overall volume.\n\n[app/api/chat/route.ts](/shadcn-ui/chatbot-template/blob/main/app/api/chat/route.ts)streams responses with`streamText`\n\n[components/chat.tsx](/shadcn-ui/chatbot-template/blob/main/components/chat.tsx)renders the conversation with`useChat`\n\nand shadcn chat primitives.[tools/](/shadcn-ui/chatbot-template/blob/main/tools)defines the tools — one file per tool (the filename is the model-facing tool name), composed in[tools/index.ts](/shadcn-ui/chatbot-template/blob/main/tools/index.ts): a server-executed GitHub repo lookup, the interactive`ask_user`\n\nquestionnaire, and provider-native web search.\n\nAssistant messages are a list of typed parts. [components/chat-message.tsx](/shadcn-ui/chatbot-template/blob/main/components/chat-message.tsx) switches on `part.type`\n\nand delegates each one to a component in [components/parts/](/shadcn-ui/chatbot-template/blob/main/components/parts):\n\n| Part type | Component | Renders |\n|---|---|---|\n`text` |\n|\n\n`tool-github_repo`\n\n[github-repo-part.tsx](/shadcn-ui/chatbot-template/blob/main/components/parts/github-repo-part.tsx)`tool-web_search`\n\n[web-search-part.tsx](/shadcn-ui/chatbot-template/blob/main/components/parts/web-search-part.tsx)`tool-ask_user`\n\n[ask-user-part.tsx](/shadcn-ui/chatbot-template/blob/main/components/parts/ask-user-part.tsx)[question-card.tsx](/shadcn-ui/chatbot-template/blob/main/components/question-card.tsx), pinned to the scroller bottom.`source-url`\n\n[sources-part.tsx](/shadcn-ui/chatbot-template/blob/main/components/parts/sources-part.tsx)Tool parts move through states as the stream progresses — `input-streaming`\n\n→ `input-available`\n\n→ `output-available`\n\n(or `output-error`\n\n) — and each component switches on `part.state`\n\nto show progress, results, and failures.\n\n- Create\n`tools/<name>.ts`\n\n(the filename is the model-facing tool name) exporting a`tool()`\n\nwith a`description`\n\n, an`inputSchema`\n\n, and an`execute`\n\nfunction (omit`execute`\n\nfor tools the user answers in the UI, like`ask_user`\n\n), then register it in[tools/index.ts](/shadcn-ui/chatbot-template/blob/main/tools/index.ts). - Add a part component in\n[components/parts/](/shadcn-ui/chatbot-template/blob/main/components/parts)and a`case \"tool-<name>\"`\n\nin[chat-message.tsx](/shadcn-ui/chatbot-template/blob/main/components/chat-message.tsx).\n\nMessage types are inferred from the tool definitions via `InferUITools`\n\n, so `part.input`\n\nand `part.output`\n\nare fully typed in your part component — renaming a tool field is a build error, not a silent `undefined`\n\n.\n\n```\nnpx shadcn@latest add button\n```\n\nMIT — see [LICENSE](/shadcn-ui/chatbot-template/blob/main/LICENSE).", "url": "https://wpnews.pro/news/a-minimal-chatbot-template-built-with-next-js", "canonical_source": "https://github.com/shadcn-ui/chatbot-template", "published_at": "2026-08-12 08:14:08+00:00", "updated_at": "2026-08-12 08:41:27.594266+00:00", "lang": "en", "topics": ["ai-tools", "developer-tools", "ai-products"], "entities": ["Vercel", "Next.js", "AI SDK", "shadcn/ui", "shadcn/react", "shadcn/typeset", "Vercel AI Gateway", "Upstash"], "alternates": {"html": "https://wpnews.pro/news/a-minimal-chatbot-template-built-with-next-js", "markdown": "https://wpnews.pro/news/a-minimal-chatbot-template-built-with-next-js.md", "text": "https://wpnews.pro/news/a-minimal-chatbot-template-built-with-next-js.txt", "jsonld": "https://wpnews.pro/news/a-minimal-chatbot-template-built-with-next-js.jsonld"}}