·15 min read

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. useAppConfig handles user preferences (theme, language, model defaults). useAccessStore manages API keys, access tokens, and server configuration. useChatStore owns chat sessions, messages, and streaming state. usePromptStore manages 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, and models() 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

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 LLMApi abstraction 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

  1. 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.

  2. Always set CODE. Without an access password, anyone who finds your Vercel URL can use your API keys. A single CODE environment variable prevents unauthorized access. Use comma-separated values for per-user passwords.

  3. Use CUSTOM_MODELS to 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.

  4. 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.

  5. 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.

  6. 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.

  7. 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

  1. Deploy to Vercel first (2 minutes). Use it for a week. If you like it, migrate to Docker for production.
  2. Set CODE and CUSTOM_MODELS before sharing the URL with anyone. These two variables prevent 90% of common problems.
  3. Create masks for your most common workflows (code review, writing, analysis, brainstorming). Masks save 30-60 seconds per conversation.
  4. Use conversation branching liberally. Every time you want to explore an alternative approach, fork the conversation. It costs nothing and preserves your original thread.
  5. Enable cloud sync (UpStash) if you use NextChat on multiple devices. The free tier handles most personal use cases.
  6. Export important conversations as Markdown or JSON. IndexedDB is not a backup strategy.
  7. 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

NL

Written by Nivant Labs Team

Engineer at Nivant Labs

Share this post