Skip to main content

Overview

The Lichess client is built with TypeScript, Snabbdom (virtual DOM), and Sass. The UI is organized as a pnpm monorepo workspace with 30+ packages in the ui/ directory.

Technology Stack

  • TypeScript: Type-safe JavaScript
  • Snabbdom: Lightweight virtual DOM library
  • Sass: CSS preprocessing
  • esbuild: Fast JavaScript bundler
  • pnpm: Fast, disk-efficient package manager

UI Directory Structure

Workspace Structure

The UI is a pnpm monorepo defined in /pnpm-workspace.yaml:
Each package in ui/ has its own package.json describing:
  • Source files
  • Dependencies (internal and external)
  • Build configuration

Internal Dependencies

Packages depend on each other using workspace references:

Building the UI

Build Script

The ui/build script is the main entry point for building UI assets:

Watch Mode

Start the build system in watch mode to continuously rebuild on changes:
When changes compile successfully, you’ll see:
The manifest lists public JavaScript and CSS assets. The server communicates updated URLs in subsequent responses.
Just reload your browser to see the changes - no server restart needed!

Building Specific Packages

Build only specific packages:

Production Build

Create an optimized production build:

Clean Build

Remove build artifacts and rebuild:

Package Configuration

Each package’s package.json includes a custom build property describing how assets are generated.

Bundle Property

Defines JavaScript modules to create as entry points:
This matches files like:
  • src/analyse.ts
  • src/analyse.nvui.ts
  • src/analyse.study.ts
Naming rule: Source filenames must be prefixed by their package name.

Bundle with Inline Scripts

For critical rendering path code:
Inline scripts are injected into <script> tags to avoid FOUC (Flash of Unstyled Content).

Sync Property

Defines filesystem copies for assets:
Useful for:
  • Copying npm packages to /public/npm
  • Linking local package versions during development

Hash Property

Defines assets to content-hash for CDN caching:
Creates symlinks in /public/hashed/ with content-based filenames.

Import Resolution

TypeScript (tsc)

Uses exports in package.json for type checking:
Import barrel exports:

esbuild

Uses only the source value for bundling:
esbuild performs code splitting to create shared chunks across modules.

Development Workflow

Testing

Run UI tests with the test runner:
See Testing for details.

Key Packages

lib

ui/lib/ Shared library code:
  • Common utilities
  • Chess engine interface
  • WebSocket handling
  • xhr helpers

common

ui/common/ Common UI components and helpers.

analyse

ui/analyse/ Analysis board interface:
  • Move tree navigation
  • Computer evaluation display
  • Study interface

round

ui/round/ Live game interface:
  • Move input
  • Clock display
  • Game controls (resign, draw, takeback)

puzzle

ui/puzzle/ Puzzle training interface:
  • Puzzle presentation
  • Solution verification
  • Rating updates

site

ui/site/ Core site UI:
  • Navigation
  • User menu (dasher)
  • Notifications

Working with Assets

Images and Fonts

Static assets go in /public/:
Reference in Sass:

Stylesheets

Sass files in each package:

Linked Packages

For development on external packages (e.g., chessground, pgn-viewer):

Build System Internals

The build system source is in ui/.build/:
Runs with:

Troubleshooting

Changes Not Appearing

  1. Check build output for errors
  2. Clear browser cache (hard refresh: Ctrl+Shift+R)
  3. Verify manifest was updated in /public/compiled/

Build Errors

pnpm Issues

Node Version

Ensure Node.js v24.11.1+:

Next Steps

Additional Resources