Overview
Lichess’s frontend is built with TypeScript and Snabbdom, a lightweight virtual DOM library. The UI is organized as a pnpm monorepo with 35+ packages, each handling specific features. The architecture emphasizes performance, modularity, and real-time updates.Technology Stack
Core Technologies
- TypeScript 5.9+: Type-safe JavaScript development
- Snabbdom 3.5.1: Minimal virtual DOM library for efficient rendering
- esbuild: Ultra-fast JavaScript bundler
- pnpm: Fast, disk-efficient package manager
- Chessground: Custom chess board UI component
- Sass: CSS preprocessing
Key Libraries
Monorepo Structure
The UI is organized as a pnpm workspace defined in/pnpm-workspace.yaml:
Package Dependencies
Packages declare dependencies using workspace protocol:Virtual DOM with Snabbdom
Why Snabbdom?
Lichess chose Snabbdom for its:- Minimal size: ~200 lines of core code
- High performance: Fast diffing algorithm
- Flexibility: Extensible module system
- Simplicity: Easy to understand and debug
Snabbdom Basics
Snabbdom uses a hyperscript functionh() to create virtual DOM nodes:
View Functions
UI components are pure functions returning VNodes:Rendering Cycle
- State change: User action or server event updates controller state
- Redraw trigger: Controller calls
redraw()function - View render: View function generates new VNode tree
- Diff & patch: Snabbdom diffs against previous VNode and patches DOM
UI Package Architecture
Core Pattern: MVC-style Controllers
Most UI packages follow an MVC-inspired pattern:Controller Example
Fromui/analyse/src/ctrl.ts:
Bootstrap Pattern
Each page bootstraps its UI from server-provided data:Shared Library (ui/lib)
The lib package provides common utilities used across all UI packages:
Core Utilities
Core Utilities
Game Utilities
Game Utilities
Chess Evaluation (ceval)
Chess Evaluation (ceval)
Client-side Stockfish integration:
Chessground Integration
Chessground is Lichess’s custom-built chess board component:- SVG-based piece rendering
- Touch and mouse input
- Premoves and conditional moves
- Animation and highlighting
- Mobile-optimized
Build System
The custom build system inui/.build/ uses esbuild:
Package Configuration
Each package defines build configuration inpackage.json:
Build Properties
bundle: Entry points for esbuild- Matches
analyse.ts,analyse.study.ts, etc. - Output:
/public/compiled/analyse.[hash].js
- Useful for large files (Stockfish WASM)
- Watch mode syncs on changes
- Creates symlinks in
/public/hashed/ - Manifest tracks hashes for server
Build Commands
Code Splitting
esbuild automatically splits shared code into chunks:TypeScript Configuration
Base TypeScript config inui/tsconfig.base.json:
State Management
Lichess doesn’t use Redux or similar state libraries. State is managed in controllers:Local State Pattern
Communication Between Components
Components communicate via:- Direct calls: Parent controller calls child methods
- Callbacks: Child triggers parent callback
- PubSub:
lib/pubsubfor global events
Performance Optimizations
Virtual DOM Efficiency
Virtual DOM Efficiency
- Keys: Use keys for list items to enable efficient reordering
- Memoization: Cache expensive view computations
- Lazy rendering: Defer off-screen content
Asset Optimization
Asset Optimization
- Code splitting: Shared chunks cached across pages
- Tree shaking: Dead code elimination
- Content hashing: Immutable URLs for CDN caching
- Lazy loading: Dynamic imports for large features
Browser Compatibility
Browser Compatibility
Browserslist targets modern browsers:No polyfills for older browsers - encourages upgrades for security.
Testing
Unit tests use Node.js test runner:ui/*/tests/**/*.test.ts
See Also
- Backend Architecture - Scala server architecture
- WebSocket Architecture - Real-time updates
- Deployment - Frontend asset delivery

