Project Structure
LibreChat is a monorepo with the following workspaces:| Workspace | Language | Side | Purpose |
|---|---|---|---|
/api | JS (legacy) | Backend | Express server — minimize changes here |
/packages/api | TypeScript | Backend | New backend code lives here (TS only) |
/packages/data-schemas | TypeScript | Backend | Database models/schemas |
/packages/data-provider | TypeScript | Shared | Shared API types, endpoints, data-service |
/client | TypeScript/React | Frontend | Frontend SPA |
/packages/client | TypeScript | Frontend | Shared frontend utilities |
Workspace Boundaries
- Keep
/apichanges to the absolute minimum (thin JS wrappers calling into/packages/api) - Database-specific shared logic goes in
/packages/data-schemas - Frontend/backend shared API logic goes in
/packages/data-provider - Build data-provider from project root:
npm run build:data-provider
Code Style Principles
Structure and Clarity
Never-nesting
Use early returns, flat code, minimal indentation. Break complex operations into well-named helpers.
Functional First
Prefer pure functions, immutable data,
map/filter/reduce over imperative loops.Only use OOP when it clearly improves domain modeling or state encapsulation.No Dynamic Imports
Avoid dynamic imports unless absolutely necessary.
DRY Principle
Don’t Repeat Yourself:
- Extract repeated logic into utility functions
- Use reusable hooks/higher-order components
- Parameterize helpers instead of duplicating
- Centralize constants and configuration
Iteration and Performance
Performance Guidelines:- Consolidate sequential O(n) operations into a single pass
- Never loop over the same collection twice if work can be combined
- Use
Map/Setfor lookups instead ofArray.find/Array.includes - Avoid unnecessary object creation
- Prevent memory leaks: careful with closures, dispose resources/event listeners, no circular references
Type Safety
Never use `any`
Explicit types for all parameters, return values, and variables.
Limit `unknown`
Avoid
unknown, Record<string, unknown>, and as unknown as T assertions.A Record<string, unknown> almost always signals a missing explicit type definition.Don't duplicate types
Before defining a new type, check if it exists in
packages/data-provider.Reuse and extend existing types rather than creating redundant definitions.Use proper types
- Use union types, generics, and interfaces appropriately
- All TypeScript and ESLint warnings/errors must be addressed
- No unresolved diagnostics
Comments and Documentation
Philosophy: Write self-documenting code.- No inline comments narrating what code does
- JSDoc only for complex/non-obvious logic or intellisense on public APIs
- Single-line JSDoc for brief docs:
/** Brief description */ - Multi-line JSDoc for complex cases with parameters and return types
- Avoid standalone
//comments unless absolutely necessary
Import Order
Imports are organized into three sections:Package imports
Sorted shortest to longest line length.
reactis always first- Multi-line imports count total character length across all lines
Type imports
Sorted longest to shortest line length.
- Package types first, then local types
- Length sorting resets between sub-groups
- Always use standalone
import type { ... } - Never inline
typeinside value imports
ESLint automatically enforces these conventions with
npm run lint --fix or pre-commit hooks.Frontend-Specific Rules
Localization
- Only update English keys in
client/src/locales/en/translation.json - Other languages are automated externally via Locize
- Use semantic key prefixes:
com_ui_,com_assistants_, etc.
React Components
- TypeScript for all React components with proper type imports
- Semantic HTML with ARIA labels (
role,aria-label) for accessibility - Group related components in feature directories (e.g.,
SidePanel/Memories/) - Use index files for clean exports
Data Management
- React Query (
@tanstack/react-query) for all API interactions - Proper query invalidation on mutations
- QueryKeys and MutationKeys in
packages/data-provider/src/keys.ts - Feature hooks pattern:
client/src/data-provider/[Feature]/queries.ts
- Endpoints:
packages/data-provider/src/api-endpoints.ts - Data service:
packages/data-provider/src/data-service.ts - Types:
packages/data-provider/src/types/queries.ts - Use
encodeURIComponentfor dynamic URL parameters
Performance Optimization
- Prioritize memory and speed efficiency at scale
- Cursor pagination for large datasets
- Proper dependency arrays to avoid unnecessary re-renders
- Leverage React Query caching and background refetching
ESLint Configuration
The project uses a comprehensive ESLint configuration defined ineslint.config.mjs:
Key Rules
Common Rules
Common Rules
prettier/prettier: ‘error’ - Enforce Prettier formattingno-nested-ternary: ‘warn’ - Avoid complex nested ternariesno-unused-vars: Warns on unused variables (except those prefixed with_)import/no-cycle: ‘error’ - Prevent circular dependenciesimport/no-self-import: ‘error’ - Prevent self-imports
TypeScript Rules
TypeScript Rules
@typescript-eslint/no-explicit-any: ‘off’ (but avoidanyin practice)@typescript-eslint/no-unused-vars: Warns on unused variables@typescript-eslint/no-unnecessary-condition: ‘off’@typescript-eslint/strict-boolean-expressions: ‘off’
React Rules
React Rules
react/react-in-jsx-scope: ‘off’ - Not needed in modern Reactreact-hooks/rules-of-hooks: ‘error’ - Enforce hooks rulesreact-hooks/exhaustive-deps: ‘warn’ - Check hook dependenciesreact/prop-types: ‘off’ - Use TypeScript instead
i18n Rules (Client Only)
i18n Rules (Client Only)
i18next/no-literal-string: ‘error’ - Enforce localization for all user-facing text (jsx-text-only mode)
Running Linting
Pre-commit Hooks
The project uses Husky and lint-staged to automatically lint and format code before commits: Configuration (.husky/lint-staged.config.js):
TypeScript Conversion
LibreChat is transitioning from JavaScript to TypeScript:Backend: In Progress
- Legacy Express.js server remains in
/apias JavaScript - All new backend code written in TypeScript under
/packages/api - Shared database logic in
/packages/data-schemas(TypeScript) - Shared API types/services in
/packages/data-provider(TypeScript) - Minimize direct changes to
/api; prefer adding TS code to/packages/api
Best Practices Summary
Do:
- Write TypeScript for all new code
- Use early returns and flat code structure
- Prefer functional programming patterns
- Consolidate loops and iterations
- Use explicit types everywhere
- Follow import ordering conventions
- Localize all user-facing text
- Write self-documenting code
- Run linting before commits
Additional Resources
- Full coding standards:
AGENTS.md - Contributing Guide
- Testing Guidelines
- Translation Guide