chatbot Documentation

A full-featured, hackable Next.js AI chatbot built by Vercel

21,002 stars TypeScript TypeScript 1 file ยท ~2,724 tokens #ai#chatgpt#nextjs#react#redis#shadcn-ui#vercel
RAW Doc

File: README.md

Chatbot

Features

  • 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
  • Data Persistence
  • Auth.js
    • Simple and secure authentication

Model Providers

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.

AI Gateway Authentication

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.

Deploy Your Own

You can deploy your own version of Chatbot to Vercel with one click:

Running locally

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 .env file or it will expose secrets that will allow others to control access to your various AI and authentication provider accounts.

  1. Install Vercel CLI: npm i vercel
  2. Link local instance with Vercel and GitHub accounts (creates .vercel directory): vercel link
  3. Download your environment variables: vercel env pull
bash
pnpm install
pnpm db:migrate # Setup database or apply latest database changes
pnpm dev

Your app template should now be running on localhost:3000.


File: components.json

{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "radix-maia",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "",
"css": "app/globals.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
}
}


File: package.json

{
"name": "chatbot",
"version": "3.1.0",
"private": true,
"scripts": {
"dev": "next dev --turbo",
"build": "tsx lib/db/migrate && next build",
"start": "next start",
"check": "ultracite check",
"fix": "ultracite fix",
"prepare": "husky",
"db:generate": "drizzle-kit generate",
"db:migrate": "npx tsx lib/db/migrate.ts",
"db:studio": "drizzle-kit studio",
"db:push": "drizzle-kit push",
"db:pull": "drizzle-kit pull",
"db:check": "drizzle-kit check",
"db:up": "drizzle-kit up",
"test": "export PLAYWRIGHT=True && pnpm exec playwright test"
},
"dependencies": {
"@ai-sdk/otel": "^1.0.15",
"@ai-sdk/provider": "^4.0.2",
"@ai-sdk/react": "4.0.16",
"@codemirror/lang-python": "^6.2.1",
"@codemirror/state": "^6.7.0",
"@codemirror/theme-one-dark": "^6.1.3",
"@codemirror/view": "^6.43.5",
"@opentelemetry/api": "^1.9.1",
"@opentelemetry/api-logs": "^0.200.0",
"@radix-ui/react-use-controllable-state": "^1.2.3",
"@streamdown/cjk": "^1.0.3",
"@streamdown/code": "^1.0.3",
"@streamdown/math": "^1.0.2",
"@streamdown/mermaid": "^1.0.2",
"@vercel/analytics": "^2.0.1",
"@vercel/blob": "^0.24.1",
"@vercel/functions": "^3.7.5",
"@vercel/otel": "^1.12.0",
"ai": "7.0.15",
"bcrypt-ts": "^8.0.1",
"botid": "^1.5.11",
"class-variance-authority": "^0.7.1",
"classnames": "^2.5.1",
"clsx": "^2.1.1",
"cmdk": "^1.1.1",
"codemirror": "^6.0.2",
"date-fns": "^4.4.0",
"diff-match-patch": "^1.0.5",
"dotenv": "^17.4.2",
"drizzle-orm": "^0.45.2",
"fast-deep-equal": "^3.1.3",
"framer-motion": "^12.42.2",
"katex": "^0.17.0",
"lucide-react": "^0.446.0",
"motion": "^12.42.2",
"nanoid": "^5.1.16",
"next": "16.2.10",
"next-auth": "5.0.0-beta.25",
"next-themes": "^0.3.0",
"orderedmap": "^2.1.1",
"papaparse": "^5.5.4",
"postgres": "^3.4.9",
"prosemirror-example-setup": "^1.2.3",
"prosemirror-inputrules": "^1.5.1",
"prosemirror-markdown": "^1.13.4",
"prosemirror-model": "^1.25.9",
"prosemirror-schema-basic": "^1.2.4",
"prosemirror-schema-list": "^1.5.1",
"prosemirror-state": "^1.4.4",
"prosemirror-view": "^1.42.0",
"radix-ui": "^1.6.1",
"react": "19.2.7",
"react-data-grid": "7.0.0-beta.47",
"react-dom": "19.2.7",
"redis": "^6.1.0",
"resumable-stream": "^2.2.12",
"server-only": "^0.0.1",
"shiki": "^3.21.0",
"sonner": "^2.0.7",
"streamdown": "^2.3.0",
"swr": "^2.4.2",
"tailwind-merge": "^3.6.0",
"tailwindcss-animate": "^1.0.7",
"use-stick-to-bottom": "^1.1.6",
"usehooks-ts": "^3.1.1",
"zod": "^4.4.3"
},
"devDependencies": {
"@biomejs/biome": "2.5.2",
"@playwright/test": "^1.61.1",
"@tailwindcss/postcss": "^4.3.2",
"@tailwindcss/typography": "^0.5.20",
"@types/d3-scale": "^4.0.9",
"@types/node": "^24.13.2",
"@types/papaparse": "^5.5.2",
"@types/pdf-parse": "^1.1.5",
"@types/react": "^18",
"@types/react-dom": "^18",
"babel-plugin-react-compiler": "^1.0.0",
"drizzle-kit": "^0.31.10",
"husky": "^9.1.7",
"lint-staged": "^16.4.0",
"postcss": "^8",
"tailwindcss": "^4.3.2",
"tsx": "^4.23.0",
"typescript": "^5.6.3",
"ultracite": "^7.8.4"
},
"lint-staged": {
"*": "corepack pnpm run fix --"
},
"packageManager": "[email protected]"
}


File: tsconfig.json

{
"compilerOptions": {
"target": "ESNext",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "react-jsx",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/": ["./"]
},
"strictNullChecks": true
},
"include": [
"next-env.d.ts",
"/*.ts",
"
/.tsx",
".next/types/**/
.ts",
"next.config.js",
".next/dev/types/**/*.ts"
],
"exclude": ["node_modules"]
}


File: vercel-template.json

{
"products": [
{
"type": "integration",
"protocol": "storage",
"productSlug": "neon",
"integrationSlug": "neon"
},
{
"type": "integration",
"protocol": "storage",
"productSlug": "upstash-kv",
"integrationSlug": "upstash"
},
{
"type": "blob"
}
]
}


File: vercel.json

{
"framework": "nextjs"
}


File: lib/db/migrations/meta/_journal.json

{
"version": "7",
"dialect": "postgresql",
"entries": [
{
"idx": 0,
"version": "7",
"when": 1710000000000,
"tag": "0000_initial",
"breakpoints": true
}
]
}


File: .github/workflows/lint.yml

name: Lint
on:
push:

jobs:
build:
runs-on: ubuntu-22.04
strategy:
matrix:
node-version: [20]
steps:
- uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 10.32.1
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: "pnpm"
- name: Install dependencies
run: pnpm install
- name: Run check
run: pnpm check


File: .github/workflows/playwright.yml

name: Playwright Tests
on:
push:
branches: [main, master]
pull_request:
branches: [main, master]

jobs:
test:
timeout-minutes: 30
runs-on: ubuntu-latest
env:
# These should be the same ones as used in production
AUTH_SECRET: ${{ secrets.AUTH_SECRET }}
POSTGRES_URL: ${{ secrets.POSTGRES_URL }}
BLOB_READ_WRITE_TOKEN: ${{ secrets.BLOB_READ_WRITE_TOKEN }}
REDIS_URL: ${{ secrets.REDIS_URL }}

text
steps:
  - uses: actions/checkout@v4
    with:
      fetch-depth: 1

  - uses: actions/setup-node@v4
    with:
      node-version: lts/*

  - name: Install pnpm
    uses: pnpm/action-setup@v2
    with:
      version: latest
      run_install: false

  - name: Get pnpm store directory
    id: pnpm-cache
    shell: bash
    run: |
      echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT

  - uses: actions/cache@v3
    with:
      path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
      key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
      restore-keys: |
        ${{ runner.os }}-pnpm-store-

  - uses: actions/setup-node@v4
    with:
      node-version: lts/*
      cache: "pnpm"

  - name: Install dependencies
    run: pnpm install --frozen-lockfile

  - name: Cache Playwright browsers
    uses: actions/cache@v3
    id: playwright-cache
    with:
      path: ~/.cache/ms-playwright
      key: ${{ runner.os }}-playwright-${{ hashFiles('**/pnpm-lock.yaml') }}

  - name: Install Playwright Browsers
    if: steps.playwright-cache.outputs.cache-hit != 'true'
    run: pnpm exec playwright install --with-deps chromium

  - name: Run Playwright tests
    run: pnpm test

  - uses: actions/upload-artifact@v4
    if: always() && !cancelled()
    with:
      name: playwright-report
      path: playwright-report/
      retention-days: 7