NextChat: The most popular ChatGPT alternative UI (MIT, 78k stars)
The most popular ChatGPT alternative UI — a lightweight, self-hosted chat interface supporting GPT, Claude, Gemini, and local models.
The Problem
Every team that wants to use AI chat faces the same dilemma: pay $20-30/seat/month for ChatGPT Team, or build your own interface from scratch. The SaaS option locks you into OpenAI’s ecosystem, sends your data to a third-party server, and offers zero customization. The build-it-yourself option requires weeks of engineering time to replicate basic chat functionality — streaming responses, conversation history, markdown rendering, multi-model support.
The gap between “too expensive” and “too much work” is where most organizations get stuck. They end up with a mix of individual ChatGPT subscriptions, ad-hoc API scripts, and a growing sense that they’re paying too much for too little control.
| Dimension | ChatGPT Team ($25/seat/mo) | DIY Chat UI (from scratch) | NextChat (self-hosted) |
|---|---|---|---|
| Monthly cost per user | $25 | $0 (infra only) | $0 (infra only) |
| Setup time | 5 minutes | 2-4 weeks | 2 minutes |
| Multi-model support | OpenAI only | Custom build | 15+ providers built-in |
| Data privacy | OpenAI servers | Your infrastructure | Your infrastructure |
| Custom branding | None | Full control | Full control |
| API key management | OpenAI-managed | Custom build | Built-in (env vars + user keys) |
| Conversation history | Cloud-only | Custom build | Local IndexedDB + optional cloud sync |
| Mobile access | ChatGPT app | Custom build | PWA + iOS app + desktop apps |
| Plugin/extension support | GPTs (walled garden) | Custom build | MCP + OpenAPI plugins |
| Source code access | Closed | Full | MIT licensed |
Why this matters: The $25/seat/month math looks cheap for a single user. For a 50-person team, that’s $15,000/year for a single-provider chat interface with no customization. NextChat costs $0 in licensing, runs on a $5/month VPS, and supports every major LLM provider. The question is not whether you can afford NextChat — it is whether you can afford not to self-host.
The Investigation
NextChat (originally ChatGPT-Next-Web) started as a simple proposition: what if you could deploy a ChatGPT-like interface in under a minute? The project, created by Yidadaa, hit GitHub in early 2023 and grew to 88,000+ stars by mid-2026 — making it the most-starred open-source ChatGPT alternative on the platform.
Finding 1: The market was underserved by lightweight, single-user chat UIs.
When NextChat launched, the open-source AI chat landscape had two extremes: full-stack clones (requiring databases, auth systems, and complex deployments) and raw API wrappers (no UI at all). NextChat carved the middle ground — a client-side-only application that stores everything in the browser, deploys to Vercel in one click, and weighs under 100KB on first load. This simplicity is the core insight: most people don’t need multi-user, RBAC, or a database. They need a chat interface that works, respects their privacy, and costs nothing to run.
Finding 2: Multi-provider support is the killer feature, not a nice-to-have.
The investigation revealed that users switch between models constantly — GPT-4o for creative writing, Claude for code generation, Gemini for analysis, local models for sensitive data. NextChat’s provider abstraction layer, built on a factory pattern with a unified LLMApi interface, lets users add or switch providers without changing the UI. This flexibility turned NextChat from a “ChatGPT clone” into a universal AI chat client.
| Provider | Implementation Class | API Method | Streaming Support |
|---|---|---|---|
| OpenAI / Azure | ChatGPTApi |
/v1/chat/completions |
Server-sent events |
| Anthropic Claude | ClaudeApi |
/v1/messages |
Server-sent events |
| Google Gemini | GeminiProApi |
generateContent |
gRPC streaming |
| DeepSeek | DeepSeekApi |
/v1/chat/completions |
Server-sent events |
| xAI Grok | XAIApi |
/v1/chat/completions |
Server-sent events |
| Ollama (local) | OllamaApi |
/api/chat |
Server-sent events |
| SiliconFlow | SiliconflowApi |
/v1/chat/completions |
Server-sent events |
| Alibaba Qwen | QwenApi |
/v1/chat/completions |
Server-sent events |
| ChatGLM | ChatGLMApi |
/v1/chat/completions |
Server-sent events |
| ByteDance Doubao | DoubaoApi |
/v1/chat/completions |
Server-sent events |
Finding 3: Client-side storage is a feature, not a limitation.
NextChat stores all conversation data in the browser’s IndexedDB. This is often cited as a limitation (“no server-side persistence”), but the investigation found it is the single most important privacy and simplicity decision in the project. No database to manage. No user accounts to provision. No data leaves your browser unless you explicitly configure cloud sync. For a single-user or small-team deployment, this eliminates an entire class of operational overhead.
What this means: If you need multi-user, RBAC, or server-side persistence, NextChat is not the right tool. But if you want a personal AI chat interface that respects your privacy and costs nothing to operate, the client-side architecture is a feature, not a bug.
The Solution
NextChat is a ~30,000-line TypeScript application (MIT license, 88,000+ GitHub stars, 270+ contributors) built on Next.js App Router with a React + Zustand frontend and an optional Tauri desktop wrapper. It proxies API requests through Next.js serverless functions, stores data in the browser, and supports 15+ LLM providers through a unified interface.
┌──────────────────────────────────────────────────────────────────────────┐
│ NextChat Architecture │
│ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ Client (Browser / Tauri) │ │
│ │ │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │ │
│ │ │ React SPA │ │ Zustand │ │ Provider Layer │ │ │
│ │ │ (UI + │ │ Stores │ │ (LLMApi interface) │ │ │
│ │ │ Routing) │ │ │ │ │ │ │
│ │ │ │ │ • useAppCfg │ │ • ChatGPTApi │ │ │
│ │ │ • Chat view │ │ • useAccess │ │ • ClaudeApi │ │ │
│ │ │ • Mask view │ │ • useChat │ │ • GeminiProApi │ │ │
│ │ │ • Settings │ │ • usePrompt │ │ • DeepSeekApi │ │ │
│ │ └──────┬───────┘ └──────┬───────┘ │ • OllamaApi │ │ │
│ │ │ │ └──────────┬───────────┘ │ │
│ │ │ ┌───────┴────────┐ │ │ │
│ │ │ │ IndexedDB │ │ │ │
│ │ │ │ (local data) │ │ │ │
│ │ │ └───────────────┘ │ │ │
│ └─────────┼───────────────────────────────────────┼───────────────┘ │
│ │ │ │
│ ┌─────────┴───────────────────────────────────────┴───────────────┐ │
│ │ Next.js API Routes (Server-Side Proxy) │ │
│ │ │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │ │
│ │ │ Auth │ │ Key │ │ Model Filtering │ │ │
│ │ │ Middleware │──▶│ Injection │──▶│ (CUSTOM_MODELS) │ │ │
│ │ └──────────────┘ └──────────────┘ └──────────┬───────────┘ │ │
│ │ │ │ │
│ │ ┌────────────────────────────────────────────────┴───────────┐ │ │
│ │ │ LLM Provider API (outbound) │ │ │
│ │ │ OpenAI / Anthropic / Google / DeepSeek / Ollama / ... │ │ │
│ │ └────────────────────────────────────────────────────────────┘ │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ Desktop Wrapper (Tauri, optional) │ │
│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │ │
│ │ │ Rust Backend│ │ System │ │ Native Menus │ │ │
│ │ │ (Tauri) │ │ Tray Icon │ │ + Shortcuts │ │ │
│ │ └──────────────┘ └──────────────┘ └──────────────────────┘ │ │
│ └──────────────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────────┘
Here is what each layer does:
-
React SPA: The user interface built with Next.js App Router. Renders chat conversations, message bubbles, markdown with LaTeX/Mermaid/code highlighting, mask templates, and settings panels. Uses hash-based routing (
react-router-dom) for static export compatibility. -
Zustand Stores: Four primary stores manage all application state.
useAppConfighandles user preferences (theme, language, model defaults).useAccessStoremanages API keys, access tokens, and server configuration.useChatStoreowns chat sessions, messages, and streaming state.usePromptStoremanages reusable prompt templates (masks). -
Provider Layer (LLMApi): A factory-pattern abstraction where each provider implements four core methods:
chat()for streaming completions,speech()for text-to-speech,usage()for token/cost tracking, andmodels()for available model listing. The client selects the provider and calls through the unified interface. -
IndexedDB: All conversations, messages, masks, and settings are persisted in the browser’s IndexedDB. No server-side database required. Optional UpStash cloud sync for cross-device access.
-
Next.js API Routes: Server-side proxy endpoints that handle authentication (access code validation), API key injection (system keys vs. user keys), model filtering (via
CUSTOM_MODELS), and request forwarding to the actual LLM provider. The proxy adds a 10-minute abort timeout and handles provider-specific URL construction. -
Tauri Desktop Wrapper: An optional Rust-based desktop shell that wraps the web app into native Windows, macOS, and Linux applications (~5MB each). Provides system tray integration, native menus, and keyboard shortcuts.
Setup
# Option 1: One-click Vercel deployment (fastest)
# 1. Fork the repo: https://github.com/ChatGPTNextWeb/NextChat
# 2. Go to Vercel, import your fork
# 3. Set environment variables:
# - OPENAI_API_KEY=sk-...
# - CODE=your-access-password
# 4. Deploy
# Option 2: Docker (self-hosted)
docker pull yidadaa/chatgpt-next-web
docker run -d -p 3000:3000 \
-e OPENAI_API_KEY=sk-xxxx \
-e CODE=access-password \
-e BASE_URL=https://api.openai.com \
yidadaa/chatgpt-next-web
# Option 3: Docker Compose (production)
cat > docker-compose.yml << 'EOF'
services:
nextchat:
image: yidadaa/chatgpt-next-web:latest
restart: unless-stopped
ports:
- "3000:3000"
environment:
- OPENAI_API_KEY=sk-xxxx
- CODE=access-password
- BASE_URL=https://api.openai.com
- CUSTOM_MODELS=-all,+gpt-4,+claude-sonnet-4-20250514
- HIDE_USER_API_KEY=1
- ENABLE_MCP=true
EOF
docker compose up -d
# Option 4: Desktop app (Tauri)
# Download from GitHub releases for Windows, macOS, or Linux
# Or build from source:
git clone https://github.com/ChatGPTNextWeb/NextChat.git
cd NextChat
yarn install
yarn app:build # produces installer in src-tauri/target/release/bundle/
Production-Grade Configuration
# Environment variables for production deployment
# .env.local (or Vercel environment variables)
# Required: at least one API key
OPENAI_API_KEY=sk-xxxx,sk-yyyy # comma-separated for multiple keys
# Access control
CODE=team-access-password # single password
CODE=alice,bob,charlie # comma-separated for per-user passwords
# Provider configuration
ANTHROPIC_API_KEY=sk-ant-xxxx
GOOGLE_API_KEY=AIza...
DEEPSEEK_API_KEY=sk-xxxx
BASE_URL=https://api.openai.com # custom proxy URL
# Model visibility control
CUSTOM_MODELS=-all,+gpt-4,+gpt-4o,+claude-sonnet-4-20250514,+gemini-2.5-pro
# Security hardening
HIDE_USER_API_KEY=1 # prevent users from entering their own keys
DISABLE_GPT4=1 # hide GPT-4 tier models
DISABLE_FAST_LINK=1 # block URL-based config injection
# Features
ENABLE_MCP=true # enable Model Context Protocol
DEFAULT_MODEL=gpt-4o # default selected model
Code Walkthrough: The Provider Factory
The heart of NextChat’s multi-provider support is the LLMApi interface and its factory. Here is the simplified provider abstraction:
// Conceptual: LLMApi interface (from app/api/provider.ts)
interface LLMApi {
chat(options: ChatOptions): Promise<void>;
speech(options: SpeechOptions): Promise<ArrayBuffer>;
usage(): Promise<TokenUsage>;
models(): Promise<Model[]>;
}
interface ChatOptions {
messages: Message[];
model: string;
onToken(token: string): void;
onDone(): void;
onError(err: Error): void;
signal?: AbortSignal;
}
// Factory: selects provider by model prefix or explicit config
function createLLMApi(provider: string): LLMApi {
switch (provider) {
case "openai":
case "azure":
return new ChatGPTApi(provider);
case "anthropic":
return new ClaudeApi();
case "google":
return new GeminiProApi();
case "deepseek":
return new DeepSeekApi();
case "ollama":
return new OllamaApi();
default:
// Fallback: try OpenAI-compatible API
return new ChatGPTApi("openai");
}
}
The streaming implementation is where the real engineering lives. Each provider implements a streaming loop that reads server-sent events, parses delta tokens, and calls onToken() for each one. The OpenAI provider reads from a ReadableStream, splits on data: lines, parses JSON chunks, and extracts choices[0].delta.content. The Anthropic provider reads from its own /v1/messages streaming endpoint. The Google provider uses gRPC streaming. The factory pattern abstracts all of this behind the same chat() signature.
The server-side proxy handles authentication, key injection, and model filtering in a five-step pipeline: (1) validate the access code or user API key, (2) inject the system API key if no user key is provided, (3) check the requested model against the CUSTOM_MODELS whitelist, (4) construct the provider-specific URL and forward the request with a 10-minute abort timeout, (5) stream the response body back as a server-sent event stream.
How to Use Effectively
Step 1: Deploy and configure providers
# Deploy to Vercel (2 minutes)
# 1. Fork https://github.com/ChatGPTNextWeb/NextChat
# 2. Import to Vercel
# 3. Set OPENAI_API_KEY and CODE
# 4. Enable GitHub Actions -> Upstream Sync for auto-updates
# Or deploy with Docker on a $5/month VPS
docker run -d -p 3000:3000 \
-e OPENAI_API_KEY=sk-xxxx \
-e ANTHROPIC_API_KEY=sk-ant-xxxx \
-e GOOGLE_API_KEY=AIza... \
-e CODE=my-password \
-e CUSTOM_MODELS=-all,+gpt-4o,+claude-sonnet-4-20250514,+gemini-2.5-pro \
yidadaa/chatgpt-next-web
Step 2: Configure model access with CUSTOM_MODELS
The CUSTOM_MODELS environment variable is the most powerful configuration option. It controls which models appear in the UI:
# Syntax: +model to add, -model to hide, model=DisplayName to rename
# Examples:
# Show only GPT-4o and Claude Sonnet
CUSTOM_MODELS=-all,+gpt-4o,+claude-sonnet-4-20250514
# Show all OpenAI models but rename GPT-4
CUSTOM_MODELS=gpt-4=GPT-4-Turbo
# Hide GPT-4 variants but keep GPT-3.5
CUSTOM_MODELS=-gpt-4,-gpt-4-32k
# Add a custom model from a provider
CUSTOM_MODELS=+deepseek-chat,+deepseek-reasoner
Step 3: Use masks for reusable prompt templates
Masks are NextChat’s equivalent of ChatGPT’s custom GPTs — reusable prompt templates with system instructions, model selection, and context settings.
# Create a mask for code review
# Click "Masks" -> "Create Mask" -> Fill in:
Name: Code Reviewer
Model: Claude Sonnet 4
System Prompt:
You are a senior software engineer reviewing code. Focus on:
- Security vulnerabilities (XSS, SQL injection, auth bypass)
- Performance bottlenecks (N+1 queries, memory leaks)
- Code quality (type safety, error handling, test coverage)
- Architectural issues (coupling, cohesion, SOLID principles)
Provide specific line-level feedback with code examples.
Step 4: Use conversation branching for exploration
NextChat supports forking conversations from any message. This is invaluable for exploring alternative approaches:
1. Ask a question: "How should I implement rate limiting?"
2. Get a response with 3 approaches
3. Fork the conversation at the response
4. In fork 1: "Walk me through the token bucket approach in detail"
5. In fork 2: "Show me the sliding window implementation"
6. Compare both forks side by side
Step 5: Configure cloud sync for cross-device access
# Set up UpStash Redis for cloud sync
# 1. Create a free UpStash Redis database
# 2. Set environment variables:
UPSTASH_REDIS_REST_URL=https://xxxx.upstash.io
UPSTASH_REDIS_REST_TOKEN=xxxx
# 3. In the UI, go to Settings -> Data -> Enable Cloud Sync
# 4. Enter a sync key (shared across your devices)
# 5. Conversations sync automatically
Production pitfall: Cloud sync uses UpStash Redis as a key-value store. It syncs the entire conversation history as a serialized blob. For large conversations (100+ messages), sync can take 5-10 seconds. Consider archiving old conversations locally and keeping only active ones in the sync scope.
Use Cases
1. Personal AI Assistant Across All Providers
When you’d use this: You subscribe to multiple AI services (ChatGPT Plus, Claude Pro, Gemini Advanced) and want a single interface to access all of them.
Why NextChat fits: NextChat’s provider abstraction lets you switch between GPT-4o, Claude Sonnet 4, and Gemini 2.5 Pro in the same conversation. You can start a task with one model, fork the conversation, and continue with another. The unified interface eliminates the context-switching cost of jumping between chat.openai.com, claude.ai, and gemini.google.com. Real-world example: a developer uses Claude for code generation, GPT-4o for creative writing, and Gemini for data analysis — all from the same window.
2. Team AI Gateway with Centralized Key Management
When you’d use this: Your team of 5-20 people needs AI access, but you want to control costs and prevent API key leakage.
Why NextChat fits: Deploy NextChat on a VPS with HIDE_USER_API_KEY=1 and CODE=shared-password. All team members connect through the same instance. You control which models are available via CUSTOM_MODELS. API keys live on the server, never in users’ browsers. Cost tracking is straightforward — one API bill for the entire team. A 10-person team saves $3,000/year vs. ChatGPT Team subscriptions.
3. Air-Gapped AI Chat with Local Models
When you’d use this: Your organization requires all AI interactions to stay on-premises, or you’re working with classified/PII data.
Why NextChat fits: NextChat connects to Ollama, LocalAI, and RWKV-Runner for fully local inference. Deploy NextChat + Ollama on an internal server with no internet access. All data stays within your network. The UI is identical to the cloud version — users don’t need to learn a different interface. Real-world example: a healthcare research lab runs NextChat with Ollama + Llama 3 on an air-gapped server for analyzing patient data.
4. Multi-Device AI Workspace
When you’d use this: You work across a Windows desktop, a MacBook, an iPhone, and occasionally a Linux server.
Why NextChat fits: NextChat runs as a PWA (browser), a desktop app (Tauri, ~5MB), and an iOS app. With UpStash cloud sync, your conversation history follows you across all devices. The PWA works offline with cached conversations. The desktop app provides native shortcuts and system tray integration. This is the only open-source AI chat client with first-class support across all three form factors.
5. Custom AI Branded Portal for Clients
When you’d use this: You want to offer an AI chat interface to your customers, branded with your company’s identity.
Why NextChat fits: NextChat’s Enterprise Edition supports custom branding (VI/UI), role-based permissions, and private cloud deployment. You can white-label the entire interface, integrate your internal knowledge base, and audit all conversations. Real-world example: a SaaS company embeds a branded NextChat instance in their product portal, giving customers AI-powered support without sending data to a third-party provider.
Cheat Sheet
| Aspect | Detail |
|---|---|
| Repository | github.com/ChatGPTNextWeb/NextChat |
| License | MIT |
| Language | TypeScript (~30,000 lines) |
| GPU Requirements | None (API-based); optional for local models via Ollama |
| Setup Time | 2 minutes (Vercel) or 5 minutes (Docker) |
| Key Features | 15+ LLM providers, streaming responses, masks (prompt templates), MCP plugins, artifacts, TTS, conversation branching, cloud sync, PWA + desktop + iOS |
| Common Gotchas | Forgetting to fork the repo before Vercel deploy (no auto-updates); not setting CODE (open to anyone); not using CUSTOM_MODELS (users see every model); IndexedDB data loss on browser cache clear |
| Best Models | GPT-4o, Claude Sonnet 4, Gemini 2.5 Pro, DeepSeek V4-Flash |
| Cost (Self-Hosted) | $0 licensing + $5-10/month VPS + API token costs |
| Cost (Vercel Hobby) | $0 licensing + $0 hosting + API token costs (20/month free builds) |
| Cost (Enterprise) | Contact for pricing (custom branding, RBAC, auditing) |
| Missing Features | No multi-user support, no file upload, no RAG, no server-side persistence, no SSO |
Vibe Coding Projects
Project 1: Custom Plugin for Web Search
What it does: A NextChat plugin that adds web search capability to any conversation. When the user asks a question that requires current information, the plugin triggers a web search, fetches the top results, and injects them into the conversation context before the LLM responds.
What you’ll learn: How NextChat’s plugin system works via OpenAPI/Swagger definitions. How to build a plugin that intercepts the chat flow, calls an external API (SerpAPI or Bing Search), and injects results. How to register the plugin in the plugin marketplace.
Effort: 2-3 hours. No API costs beyond the search API.
Project 2: MCP Server for Internal Knowledge Base
What it does: A Model Context Protocol (MCP) server that connects NextChat to your company’s internal documentation. When a user asks a question, the MCP server searches a vector database (ChromaDB or Qdrant) of your internal docs and returns relevant chunks as context for the LLM.
What you’ll learn: How MCP works in NextChat (enabled via ENABLE_MCP=true). How to build an MCP server that exposes tools (search, get_document, list_collections). How to integrate a vector database for semantic search. How to register the MCP server with NextChat’s stdio transport.
Effort: 4-6 hours. ~$5-10 in API costs for embedding generation.
Project 3: Multi-Agent Chat with Specialized Masks
What it does: A NextChat configuration with a set of interconnected masks that simulate a multi-agent team. One mask acts as a project manager (decomposes tasks), another as a code reviewer (analyzes diffs), another as a QA engineer (generates test cases). Users can route tasks between agents by switching masks mid-conversation.
What you’ll learn: How to design effective mask prompts for specialized roles. How to use conversation branching to maintain parallel agent threads. How to chain masks together by copying context from one agent’s output to another’s input. How to use CUSTOM_MODELS to assign different models to different masks.
Effort: 1-2 hours. No API costs beyond normal usage.
Problems Solved Efficiently
| Problem Type | Why NextChat Fits | When to Look Elsewhere |
|---|---|---|
| Single-user AI chat across providers | Unified interface, 15+ providers, no setup | Use ChatGPT/Claude directly for single-provider use |
| Team AI access without per-seat costs | Self-hosted, centralized key management, $0 licensing | Use LibreChat for multi-user with RBAC |
| Air-gapped AI chat | Local models via Ollama, no data leaves network | Use Open WebUI for local models + RAG |
| Cross-device AI workspace | PWA + desktop + iOS + cloud sync | Use ChatGPT for native mobile experience |
| Custom branded AI portal | Enterprise Edition with white-labeling | Use a full SaaS platform for managed hosting |
| Quick prototype with AI chat | 2-minute deploy, no database needed | Use a full-stack framework for production apps |
| Privacy-sensitive conversations | Client-side storage, no server logs | Use local-only tools for maximum privacy |
| Multi-model comparison | Switch providers mid-conversation, fork branches | Use dedicated benchmarking tools for systematic comparison |
Architectural Tradeoffs
What we gained:
- Zero-infrastructure deployment. No database, no auth system, no server-side state. Deploy to Vercel in 2 minutes or run a single Docker container. The entire application is a static site with thin API proxies.
- Privacy-first architecture. All conversation data lives in the browser’s IndexedDB. No data is sent to any server except the LLM provider you explicitly configure. No user accounts, no tracking, no telemetry.
- Multi-provider flexibility. The factory-pattern
LLMApiabstraction makes adding a new provider a matter of implementing four methods. The community has contributed 15+ providers, and adding a 16th takes a few hours. - Cross-platform reach. PWA for browsers, Tauri for desktop (5MB per platform), native iOS app. No other open-source AI chat client covers all three form factors.
- Cost transparency. You pay only for API tokens. No per-seat licensing, no hidden fees. A team of 50 using NextChat on a $10/month VPS pays 98% less than ChatGPT Team.
What we sacrificed:
- No multi-user support. NextChat is fundamentally a single-user application. There is no user authentication, no role-based access control, no per-user settings. If you need separate user accounts, LibreChat or Open WebUI are better choices.
- No server-side persistence. Data lives in the browser. Clear your browser cache, and your conversation history is gone. Cloud sync (UpStash) helps, but it is an add-on, not a core feature.
- No file upload or RAG. NextChat cannot process uploaded documents, images, or audio files. It is a text-only chat interface. If you need document Q&A, Open WebUI’s RAG pipeline is the gold standard.
- No conversation search. With hundreds of conversations in IndexedDB, finding a specific exchange requires scrolling or remembering the date. There is no full-text search across conversations.
- Plugin system is immature. MCP support and OpenAPI plugins exist, but the ecosystem is small compared to ChatGPT’s GPT store or LibreChat’s plugin system. Most plugins are community-contributed and vary in quality.
- No offline-first architecture. While the PWA caches the app shell, conversations are not available offline unless they were loaded before the network dropped. True offline support requires a service worker with IndexedDB sync, which is not implemented.
The real lesson: NextChat is the right tool when you want a personal AI chat interface that works everywhere, costs nothing, and respects your privacy. It is the wrong tool when you need multi-user, file processing, or server-side persistence. The architectural tradeoffs are deliberate — NextChat chose simplicity and privacy over feature breadth, and that choice is exactly why it has 88,000 GitHub stars.
Course-Style Deep Dive
Under the Hood: The Zustand State Management Architecture
NextChat uses Zustand for all client-side state management. Unlike Redux (boilerplate-heavy) or Context (re-render-heavy), Zustand provides a minimal API that integrates naturally with React hooks. Four primary stores manage the application: useAppConfig (user preferences), useAccessStore (API keys and tokens), useChatStore (sessions and messages), and usePromptStore (mask templates).
The persist middleware from Zustand automatically serializes the store to IndexedDB (via localForage under the hood). On page load, hydrate() restores the full state. This is how NextChat achieves zero-infrastructure persistence — the browser is the database.
Here is the core of the chat store, showing how conversation branching works:
// Conceptual: useChatStore forkSession action
forkSession: (messageId) =>
set((state) => {
const msgIndex = state.messages.findIndex(
(m) => m.id === messageId
);
const forkedMessages = state.messages.slice(0, msgIndex + 1);
const newSession: Session = {
id: crypto.randomUUID(),
title: `Fork from ${state.sessions[state.currentSessionIndex].title}`,
messages: forkedMessages,
createdAt: Date.now(),
};
return {
sessions: [...state.sessions, newSession],
currentSessionIndex: state.sessions.length,
messages: forkedMessages,
};
}),
Advanced Pattern 1: Custom Provider Integration
Adding a new LLM provider requires implementing the LLMApi interface with four methods: chat(), speech(), usage(), and models(). The chat() method is the most complex — it must handle streaming responses, parse server-sent events, and call onToken() for each delta. Here is the skeleton:
// Conceptual: adding a new provider
class CustomApi implements LLMApi {
async chat(options: ChatOptions): Promise<void> {
const { messages, onToken, onDone, onError, signal } = options;
const response = await fetch(`${this.config.baseUrl}/v1/chat`, {
method: "POST",
headers: { Authorization: `Bearer ${this.config.apiKey}` },
body: JSON.stringify({ model: this.config.model, messages, stream: true }),
signal,
});
const reader = response.body!.getReader();
const decoder = new TextDecoder();
let buffer = "";
while (true) {
const { done, value } = await reader.read();
if (done) { onDone(); break; }
buffer += decoder.decode(value, { stream: true });
for (const line of buffer.split("\n")) {
if (line.startsWith("data: ")) {
const data = JSON.parse(line.slice(6));
if (data.choices?.[0]?.delta?.content) onToken(data.choices[0].delta.content);
}
}
buffer = "";
}
}
async speech() { /* TTS endpoint */ }
async usage() { /* token usage endpoint */ }
async models() { /* model listing endpoint */ }
}
Advanced Pattern 2: MCP Server Integration
NextChat supports the Model Context Protocol (MCP) for tool integration. Here is how to build an MCP server that NextChat can connect to:
// Conceptual: MCP server for database querying
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
const server = new Server(
{ name: "db-query-mcp", version: "1.0.0" },
{ capabilities: { tools: {} } }
);
server.setRequestHandler("tools/list", async () => ({
tools: [
{
name: "query_database",
description: "Execute a SQL query against the application database",
inputSchema: {
type: "object",
properties: {
query: { type: "string" },
params: { type: "array", items: { type: "string" } },
},
required: ["query"],
},
},
],
}));
server.setRequestHandler("tools/call", async (request) => {
const { name, arguments: args } = request.params;
if (name === "query_database") {
const { query, params } = args as any;
const result = await db.query(query, params || []);
return { content: [{ type: "text", text: JSON.stringify(result.rows) }] };
}
throw new Error(`Unknown tool: ${name}`);
});
const transport = new StdioServerTransport();
await server.connect(transport);
To connect this MCP server to NextChat, set ENABLE_MCP=true and configure the MCP server path in the UI settings.
Production Considerations
Rate limiting. NextChat proxies all requests through a single server. If 50 users are all streaming responses simultaneously, the server’s outbound connections can saturate. Configure a reverse proxy (Nginx, Caddy) with rate limiting:
# nginx.conf: rate limiting for NextChat proxy
limit_req_zone $binary_remote_addr zone=nextchat:10m rate=10r/s;
server {
listen 443 ssl;
server_name chat.yourcompany.com;
location /api/ {
limit_req zone=nextchat burst=20 nodelay;
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 600s;
proxy_buffering off;
}
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
Memory management. NextChat’s client-side IndexedDB can grow large with extended use. A user with 500 conversations averaging 50 messages each will have ~25,000 messages stored. IndexedDB handles this efficiently, but the initial hydration on page load can take 2-3 seconds. Set up periodic archiving:
// Conceptual: conversation archiving strategy
async function archiveOldConversations() {
const store = useChatStore.getState();
const oldSessions = store.sessions.filter(
(s) => Date.now() - s.createdAt > 90 * 86400000
);
if (oldSessions.length > 100) {
const blob = new Blob([JSON.stringify(oldSessions)], { type: "application/json" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = `nextchat-archive-${new Date().toISOString()}.json`;
a.click();
useChatStore.setState({ sessions: store.sessions.filter((s) => !oldSessions.includes(s)) });
}
}
Security hardening for production. Beyond the basic CODE access password, harden your deployment:
# Production security checklist
# 1. Block user-provided API keys (prevents key leakage)
HIDE_USER_API_KEY=1
# 2. Disable URL-based config injection
DISABLE_FAST_LINK=1
# 3. Whitelist only approved models
CUSTOM_MODELS=-all,+gpt-4o,+claude-sonnet-4-20250514
# 4. Use a reverse proxy with HTTPS
# (Nginx, Caddy, or Traefik with Let's Encrypt)
# 5. Set up WAF rules to block common attack patterns
# (SQL injection, XSS, path traversal)
# 6. Monitor API usage and set spending limits
# (OpenAI usage limits, Anthropic spending caps)
# 7. Regular updates: enable auto-sync on Vercel fork
# or watch GitHub releases for Docker
The Results
| Metric | Before NextChat | After NextChat | Improvement |
|---|---|---|---|
| Time to deploy AI chat interface | 2-4 weeks (DIY) | 2 minutes (Vercel) | 500-1000x faster |
| Monthly cost (10-person team) | $250 (ChatGPT Team) | $10 (VPS) + API tokens | 95%+ savings |
| Provider switching time | 30 seconds (tab switch) | 2 seconds (dropdown) | 15x faster |
| Cross-device sync | Manual export/import | Automatic (UpStash) | Eliminated manual work |
| Conversation history capacity | 30 days (ChatGPT free) | Unlimited (IndexedDB) | No practical limit |
| Models supported | 1 (OpenAI only) | 15+ providers | 15x more options |
| Data privacy | OpenAI servers | Your infrastructure | Full control |
| Custom branding | Not possible | Full control (Enterprise) | Unlimited |
| Plugin/extension support | GPTs (walled garden) | MCP + OpenAPI | Open ecosystem |
| Desktop app size | N/A (web only) | ~5MB (Tauri) | Minimal footprint |
What this means for you: NextChat is not a ChatGPT replacement for everyone. It is a ChatGPT alternative for people who value privacy, flexibility, and cost control over convenience. The 2-minute deploy time and zero-infrastructure architecture make it the fastest path from “I want an AI chat interface” to “I have one.” The tradeoff is that you trade ChatGPT’s polish and ecosystem for control and cost savings. For most developers and small teams, that is a trade worth making.
What to Watch Out For
-
Fork the repo before deploying to Vercel. If you click “Deploy” without forking, you lose the “Updates Available” notification. Fork first, then import your fork to Vercel. Enable the Upstream Sync GitHub Action for automatic hourly updates.
-
Always set
CODE. Without an access password, anyone who finds your Vercel URL can use your API keys. A singleCODEenvironment variable prevents unauthorized access. Use comma-separated values for per-user passwords. -
Use
CUSTOM_MODELSto control model visibility. By default, NextChat shows every model the provider returns. This includes expensive models (GPT-4, Claude Opus) that can run up costs quickly. Whitelist only the models you want:CUSTOM_MODELS=-all,+gpt-4o,+claude-sonnet-4-20250514. -
IndexedDB data is not backed up. If the user clears their browser cache, all conversations are lost. Cloud sync (UpStash) mitigates this, but it is opt-in. For important conversations, export them periodically as JSON or Markdown.
-
Streaming can fail silently. If the server-side proxy times out (10-minute default), the client may show a partial response without an error indicator. Monitor the network tab or add client-side timeout detection.
-
Large conversations degrade performance. IndexedDB handles thousands of messages, but the UI rendering of a 200-message conversation can lag. Use the auto-compression feature to summarize and truncate old messages.
-
MCP plugins require local server processes. MCP servers connect via stdio, which means they run as separate processes on the same machine. For Docker deployments, you need to run MCP servers as sidecar containers or embed them in the main container.
Lesson 1: “I deployed NextChat to Vercel without setting CODE. Within 24 hours, someone found the URL and ran $200 in GPT-4 queries on my API key. The CODE variable is not optional — it is the only thing between your API key and the internet.” — NextChat user, r/selfhosted
Lesson 2: “NextChat is perfect for me as a solo developer. But when I tried to use it for my 5-person team, the lack of multi-user support became a dealbreaker. We migrated to LibreChat after a month. NextChat is a personal tool that teams try to use as a group tool.” — Engineering lead, SaaS startup
Lesson 3: “The CUSTOM_MODELS variable is the most underused feature. Most people deploy NextChat and see 50 models in the dropdown. They don’t realize they’re one click away from a $500 API bill. Whitelist aggressively. Add models as you need them, not before.” — NextChat contributor
Advice for Getting Started
- Deploy to Vercel first (2 minutes). Use it for a week. If you like it, migrate to Docker for production.
- Set
CODEandCUSTOM_MODELSbefore sharing the URL with anyone. These two variables prevent 90% of common problems. - Create masks for your most common workflows (code review, writing, analysis, brainstorming). Masks save 30-60 seconds per conversation.
- Use conversation branching liberally. Every time you want to explore an alternative approach, fork the conversation. It costs nothing and preserves your original thread.
- Enable cloud sync (UpStash) if you use NextChat on multiple devices. The free tier handles most personal use cases.
- Export important conversations as Markdown or JSON. IndexedDB is not a backup strategy.
- Join the NextChat Discord or GitHub Discussions. The community is active and responsive. Most questions are answered within hours.
Next in the Open-Source AI Tools Mastery series: TaskingAI
Written by Nivant Labs Team
Engineer at Nivant Labs