Chatbot (formerly AI Chatbot) is a free, open-source template built with Next.js and the AI SDK that helps you quickly build powerful chatbot applications.
Read Docs · Features · Model Providers · Deploy Your Own · Running locally
- Next.js App Router
- Advanced routing for seamless navigation and performance
- React Server Components (RSCs) and Server Actions for server-side rendering and increased performance
- AI SDK
- Unified API for generating text, structured objects, and tool calls with LLMs
- Hooks for building dynamic chat and generative user interfaces
- Supports OpenAI, Anthropic, Google, xAI, and other model providers via AI Gateway
- shadcn/ui
- Styling with Tailwind CSS
- Component primitives from Radix UI for accessibility and flexibility
- Data Persistence
- Neon Serverless Postgres for saving chat history and user data
- Vercel Blob for efficient file storage
- Auth.js
- Simple and secure authentication
This template uses the Vercel AI Gateway to access multiple AI models through a unified interface. Models are configured in lib/ai/models.ts with per-model provider routing. Included models: Mistral, Moonshot, DeepSeek, OpenAI, and xAI.
For Vercel deployments: Authentication is handled automatically via OIDC tokens.
For non-Vercel deployments: You need to provide an AI Gateway API key by setting the AI_GATEWAY_API_KEY environment variable in your .env.local file.
With the AI SDK, you can also switch to direct LLM providers like OpenAI, Anthropic, Cohere, and many more with just a few lines of code.
You can deploy your own version of Chatbot to Vercel with one click:
You will need to use the environment variables defined in .env.example to run Chatbot. It's recommended you use Vercel Environment Variables for this, but a .env file is all that is necessary.
Note: You should not commit your
.envfile or it will expose secrets that will allow others to control access to your various AI and authentication provider accounts.
- Install Vercel CLI:
npm i -g vercel - Link local instance with Vercel and GitHub accounts (creates
.verceldirectory):vercel link - Download your environment variables:
vercel env pull
pnpm install
pnpm db:migrate # Setup database or apply latest database changes
pnpm devYour app template should now be running on localhost:3000.
The compose stack brings up the app plus everything it stores data in — Postgres, Redis and an S3-compatible bucket — so nothing outside Docker needs to be installed and no Vercel account is required:
docker compose upThat builds the app image, waits for the backing services to report healthy,
creates the uploads bucket, applies lib/db/migrations, then starts next dev on
localhost:3000. The source tree is bind-mounted, so edits
on the host hot-reload in the container; node_modules and .next live in named
volumes so the container's Linux builds never collide with the host's.
Uploads go through a provider interface in lib/storage rather than
straight to Vercel Blob:
| Provider | Selected by | Used for |
|---|---|---|
s3 |
STORAGE_PROVIDER=s3, or any S3_BUCKET being set |
MinIO locally; AWS S3, Cloudflare R2 or any S3 API in production |
vercel-blob |
STORAGE_PROVIDER=vercel-blob, or neither being set |
Vercel deployments |
Compose defaults to s3 and points it at the bundled MinIO, so image attachments
work out of the box. The MinIO console is at
localhost:9001 (minioadmin / minioadmin).
Writes and reads take different routes locally. The app uploads straight to MinIO
over the compose network (S3_ENDPOINT), while reads come back through the app's
own origin at /storage/..., which next.config.ts rewrites to the bucket. That
indirection is there because next/image fetches the upstream image server-side:
a localhost:9000 URL would send the optimizer to the app container rather than
to MinIO. In production, leave S3_PROXY_PATH unset and point S3_PUBLIC_URL at
the bucket or a CDN domain.
Models come from a provider registry in lib/ai/providers
rather than from the Vercel AI Gateway directly. Every provider is registered
unconditionally and each reports an empty catalogue when it cannot actually
serve anything, so the picker only ever offers models that work:
| Provider | Models | Configuration |
|---|---|---|
gateway |
The curated line-up in lib/ai/models.ts |
AI_GATEWAY_API_KEY, or running on Vercel |
ollama |
Everything a local Ollama holds, plus the Ollama Cloud catalogue | None for a local server; OLLAMA_API_KEY adds cloud |
A local install and Ollama Cloud are one provider, not two — they serve the same
/api/tags, /api/show and /v1 routes, and Ollama itself lists cloud models
alongside local ones. The local server is found by probing localhost, the
container host and the ollama compose service in turn, so nothing needs
configuring: install Ollama, ollama pull a model, and it appears in the picker.
Everything about a model comes from the API rather than from the codebase.
Names are used exactly as /api/tags reports them. Capabilities come from
/api/show, so tool, vision and reasoning support is read from the server
rather than guessed, and models that cannot hold a conversation (embedding
models) are filtered out. Requests are routed by which endpoint listed a model,
and a model the local server only proxies — /api/tags marks those with a
remote_host — is sent to that host with your key rather than through a local
server that would reject it.
Model ids are namespaced provider/modelName — ollama/qwen2.5:7b,
gateway/moonshotai/kimi-k2.5 — and that whole string is what gets stored, so a
provider and a model name can never drift apart.
A provider that cannot actually run a model still lists it, marked unusable with the reason, rather than disappearing: the gateway line-up stays visible without credentials so it is obvious what setting a key would unlock.
Every job the app gives a model is a role — chat, chat titles, each artifact kind, writing suggestions — and each is chosen under Settings in the user menu. Roles left on Follow chat model use whatever the conversation is set to, so only the ones you care about need pinning.
Choices are stored per user in the ModelSettings table, one column per role,
each holding a full provider/modelName reference. They survive a reload and
follow the account rather than the browser.
Defaults live in lib/ai/models.ts. A default pointing at a provider that is not
available is skipped rather than allowed to fail, which is what lets an
Ollama-only install generate its own chat titles.
Every value has a working default, so docker compose up boots on a fresh clone
with no configuration. Copy .env.docker.example to .env
to override any of them.
Nothing outside Docker is required any more, provided a local Ollama is running: the stack discovers it and uses it for chat, titles and artifacts alike.
Two optional keys widen the model list — AI_GATEWAY_API_KEY for the Vercel
AI Gateway line-up, and OLLAMA_API_KEY for the Ollama Cloud catalogue. With neither key
and no Ollama, set MOCK_AI=True to serve canned replies from
lib/ai/models.mock.ts; everything else (auth, chat persistence, history,
uploads, artifacts) works normally.
docker compose up -d # start in the background
docker compose logs -f app # tail the dev server
docker compose exec app pnpm check # lint inside the container
docker compose exec postgres psql -U chatbot chatbot
docker compose run --rm migrate # re-apply migrations
docker compose --profile tools up -d adminer # DB browser on :8080
docker compose down # stop; add -v to also drop the dataInstalling a dependency needs a one-off container rather than exec, so pnpm does
not pull node_modules out from under the running dev server:
docker compose stop app
docker compose run --rm --no-deps --entrypoint sh app -c 'pnpm add <package>'
docker compose up -d appPostgres, Redis and MinIO are published on the host at 5432, 6379 and 9000, so
pnpm db:studio and other host-side tooling can reach them. Set PORT,
POSTGRES_PORT, REDIS_PORT or MINIO_PORT in .env if those are taken.
Note: the compose stack runs
next dev. A production build served over plain HTTP would set__Secure-auth cookies that the browser drops onlocalhost, sonext startis left to real deployments.