How to Add New Features or Models to Lobe Chat: A Complete Developer Guide

Adding new features or models to Lobe Chat involves creating a ModelProviderCard in packages/model-bank/src/modelProviders/ for backend model support, or building React components in src/features/ for UI functionality, then wiring them through the Zustand store and i18n system.

Lobe Chat is a modern AI chat application built by lobehub that separates model provider definitions from UI features through a type-safe TypeScript architecture. Whether you need to integrate a new LLM provider or build a custom interface component, understanding how to add new features or models to Lobe Chat requires navigating its provider card system, state management, and React component structure.

Understanding Lobe Chat's Architecture

Lobe Chat's architecture separates model providers (the back-end definitions of AI models) from UI features (React components that surface functionality). Adding a new model or a new front-end feature follows a predictable, type-safe flow:

  1. Model side – create a provider card, register it, and expose it through the parsing utilities.
  2. State side – the provider list is loaded into the Zustand store so the UI can enumerate it.
  3. UI side – build a feature component, add a route (if needed), and optionally expose i18n strings.

All of these steps are pure TypeScript/React; no runtime code execution is required.

Adding a New Model Provider

Step 1: Define the Provider Card

Each provider is described by a ModelProviderCard (type defined in packages/types/src/llm.ts). It contains the provider id, displayName, description, and an array of chatModels.

// packages/types/src/llm.ts
export interface ModelProviderCard {
  id: string;
  displayName: string;
  description?: string;
  chatModels: ChatModelCard[];
  // …other optional fields (e.g., `enabled`, `icon`)
}

Create a provider file under packages/model-bank/src/modelProviders/. Use an existing provider as a template (e.g., openai.ts):

// packages/model-bank/src/modelProviders/mynewprovider.ts
import type { ModelProviderCard } from '@/types/llm';
import { createChatModel } from '@/utils/model';

const MyNewProvider: ModelProviderCard = {
  id: 'mynewprovider',
  displayName: 'My New Provider',
  description: 'A custom LLM provider for demo purposes',
  chatModels: [
    createChatModel({
      id: 'my-model-1',
      displayName: 'My Model 1',
      // token limit, abilities, etc.
      contextWindowTokens: 4096,
      abilities: {
        reasoning: true,
        vision: false,
        functionCall: true,
        files: false,
      },
    }),
    // …add more models if needed
  ],
};

export default MyNewProvider;

The helper createChatModel lives in packages/model-bank/src/utils/model.ts (you can also construct the object manually).

Step 2: Register the Provider

Export the provider in the central index:

// packages/model-bank/src/modelProviders/index.ts
import MyNewProvider from './mynewprovider';
/* …other imports… */

export const DEFAULT_MODEL_PROVIDER_LIST = [
  /* existing providers … */
  MyNewProvider, // ← add here
];

Run the type-checking script (bun run type-check) to ensure the new card complies with ModelProviderCard.

Step 3: Verify Model Parsing

When a user enters a model selector string (e.g., gpt-4<8192:vision>), Lobe Chat parses it with parseModels.ts. The new provider's models are automatically recognized as long as they appear in DEFAULT_MODEL_PROVIDER_LIST.

// src/utils/server/parseModels.ts
export const parseModelString = async (
  providerId: string,
  modelString: string = '',
  withDeploymentName = false,
) => {
  // …
  const modelType = await getModelPropertyWithFallback<AiModelType>(
    id,
    'type',
    providerId,
  );
  // …
};

No further changes are needed – the parser looks up the model type via @lobechat/model-runtime, which in turn reads the provider card you added.

Step 4: State Integration

The provider list is loaded into the Zustand store (aiProvider slice). Adding a new provider automatically makes it selectable in the UI.

// src/store/aiInfra/slices/aiProvider/action.ts
const [{ LOBE_DEFAULT_MODEL_LIST }, { DEFAULT_MODEL_PROVIDER_LIST }] =
  await Promise.all([
    import('model-bank'),
    import('model-bank/modelProviders'),
  ]);
// …
const enabledAiProviders: EnabledProvider[] = DEFAULT_MODEL_PROVIDER_LIST.filter(
  (p) => p.enabled !== false,
);

Adding a New Front-End Feature

Step 1: Create the Feature Component

All UI pieces live under src/features/. A feature typically consists of a React component (TSX), optional hooks, and i18n entries.

// src/features/MyNewFeature.tsx
import React from 'react';
import { Card, Button } from 'antd';
import { useTranslation } from 'react-i18next';
import { useChatStore } from '@/store/chat';

export const MyNewFeature = () => {
  const { t } = useTranslation('default');
  const startChat = useChatStore((s) => s.startChat);

  return (
    <Card title={t('myNewFeature.title')}>
      <p>{t('myNewFeature.description')}</p>
      <Button type="primary" onClick={() => startChat('mynewprovider', 'my-model-1')}>
        {t('myNewFeature.startButton')}
      </Button>
    </Card>
  );
};

Step 2: Add Internationalization

Add i18n strings under src/locales/default/:

// src/locales/default/myNewFeature.ts
export default {
  title: 'Demo Feature',
  description: 'A quick way to start a chat with My New Model.',
  startButton: 'Start Demo Chat',
};

Re-export it in the locale index:

// src/locales/default/index.ts
import myNewFeature from './myNewFeature';
export default {
  /* …existing imports… */
  myNewFeature,
};

Step 3: Wire Into Routing

If the feature should have its own page, add a folder under src/routes/(main)/:

// src/routes/(main)/my-demo/page.tsx
import { MyNewFeature } from '@/features/MyNewFeature';

export default function Page() {
  return <MyNewFeature />;
}

The route automatically picks up the Next.js file-system routing.

Step 4: Testing Your Feature

Lobe Chat uses Vitest for unit tests. Add a test under src/features/__tests__/MyNewFeature.test.tsx:

import { render, screen, fireEvent } from '@testing-library/react';
import { MyNewFeature } from '@/features/MyNewFeature';
import { useChatStore } from '@/store/chat';

vi.mock('@/store/chat', () => ({
  useChatStore: vi.fn(() => ({
    startChat: vi.fn(),
  })),
}));

test('starts chat with the new model', () => {
  render(<MyNewFeature />);
  fireEvent.click(screen.getByRole('button'));
  expect(useChatStore().startChat).toHaveBeenCalledWith('mynewprovider', 'my-model-1');
});

Run the test: bunx vitest src/features/__tests__/MyNewFeature.test.tsx.

Complete Integration Example

Below is a minimal, end-to-end snippet showing the three main files you need to modify/create.

// packages/model-bank/src/modelProviders/mynewprovider.ts
import type { ModelProviderCard } from '@/types/llm';
import { createChatModel } from '@/utils/model';

const MyNewProvider: ModelProviderCard = {
  id: 'mynewprovider',
  displayName: 'My New Provider',
  chatModels: [
    createChatModel({
      id: 'my-model-1',
      displayName: 'My Model 1',
      contextWindowTokens: 4096,
      abilities: { reasoning: true, functionCall: true },
    }),
  ],
};

export default MyNewProvider;
// packages/model-bank/src/modelProviders/index.ts (excerpt)
import MyNewProvider from './mynewprovider';
/* …existing imports… */

export const DEFAULT_MODEL_PROVIDER_LIST = [
  /* …existing providers… */
  MyNewProvider,
];
// src/features/MyNewFeature.tsx
import React from 'react';
import { Card, Button } from 'antd';
import { useTranslation } from 'react-i18next';
import { useChatStore } from '@/store/chat';

export const MyNewFeature = () => {
  const { t } = useTranslation('default');
  const startChat = useChatStore((s) => s.startChat);

  return (
    <Card title={t('myNewFeature.title')}>
      <p>{t('myNewFeature.description')}</p>
      <Button type="primary" onClick={() => startChat('mynewprovider', 'my-model-1')}>
        {t('myNewFeature.startButton')}
      </Button>
    </Card>
  );
};

After adding these files, run bun run type-check to verify TypeScript compliance, then start the dev server with bun dev to see the new provider and feature in action.

Key Files Reference

Area File Purpose
Model definition packages/model-bank/src/modelProviders/<provider>.ts Holds the ModelProviderCard for each provider.
Provider registry packages/model-bank/src/modelProviders/index.ts DEFAULT_MODEL_PROVIDER_LIST is the source of truth for all providers.
Model parsing src/utils/server/parseModels.ts Translates user-entered strings into concrete model objects.
State loading src/store/aiInfra/slices/aiProvider/action.ts Pulls the provider list into the Zustand store.
Feature component src/features/<Feature>.tsx UI implementation of a new capability.
Routing src/routes/(main)/<slug>/page.tsx Optional page wrapper for a feature.
i18n src/locales/default/<feature>.ts Adds localized strings for the UI.
Tests src/**/*.test.tsx Vitest unit tests for components and logic.

Summary

  • Model providers are defined as ModelProviderCard objects in packages/model-bank/src/modelProviders/ and exported via DEFAULT_MODEL_PROVIDER_LIST in the index file.
  • Model parsing happens automatically through src/utils/server/parseModels.ts once the provider is registered, requiring no manual parser updates.
  • State management integrates new providers via the Zustand store in src/store/aiInfra/slices/aiProvider/action.ts, making them immediately selectable in the UI.
  • Front-end features are React components in src/features/ that consume the store and require i18n entries in src/locales/default/ for localization.
  • Testing uses Vitest; run bun run type-check and bunx vitest to validate changes before deployment.

Frequently Asked Questions

Do I need to modify the database schema to add a new provider?

No, Lobe Chat uses a runtime provider card system where models are defined in TypeScript files under packages/model-bank/src/modelProviders/. The DEFAULT_MODEL_PROVIDER_LIST array is loaded into the Zustand store at runtime, so no database migrations are required to add or update model providers.

How do I test a new model provider without API credentials?

You can verify the provider integration using bun run type-check to ensure TypeScript compliance and run bunx vitest to execute unit tests. The provider will appear in the UI's model selector even without valid API keys, allowing you to test the parsing logic and state integration; only the actual chat completion will fail when the backend attempts to call the provider's API.

Can I add a feature without adding a new provider?

Yes, front-end features in src/features/ are completely independent of the model provider system. You can build React components that utilize existing models through the useChatStore hook or implement entirely new UI capabilities such as dashboards, tools, or widgets without modifying any files in packages/model-bank/.

What testing framework does Lobe Chat use?

Lobe Chat uses Vitest for unit testing. Test files are typically co-located with source files or placed in __tests__ directories throughout the src/ folder. You can run the full test suite with bunx vitest or target specific files to validate that new features and providers integrate correctly with existing state management and UI components.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →