chess.js
A dependency-free TypeScript chess library for move generation, validation, and check/checkmate/draw detection.
Repository Health
Technical Analysis
chess.js is a zero-dependency TypeScript library that implements the full rules of chess: legal move generation and validation, piece placement and movement, and check/checkmate/stalemate/draw detection (threefold repetition, fifty-move rule, insufficient material). It exposes a single Chess class that tracks board state, move history, and game-over conditions, plus built-in FEN and PGN parsing/serialization, making it the rules-engine layer that chess UIs, bots, and analysis tools build on top of rather than reimplementing.
The library ships with no runtime dependencies, is written and typed in TypeScript, and builds to both CommonJS and ESM bundles with generated .d.ts declarations. It’s the most widely used JavaScript/TypeScript chess-logic library on npm, powering browser chessboards, server-side move validation for online play, PGN-archive tooling, and the move-generation layer of hobby chess engines and bots.
What You Get
- Full legal move generation and validation for every piece, including castling, en passant, and promotion
- Game-state detection: check, checkmate, stalemate, draw, threefold repetition, and insufficient material
- FEN parsing/validation and PGN import/export with headers, comments, and Numeric Annotation Glyphs (NAGs)
- A dependency-free TypeScript API shipped as both CJS and ESM builds with full type definitions
Common Use Cases
- Powering the rules engine behind a browser or React Native chessboard UI
- Validating moves server-side for an online chess platform or matchmaking service
- Generating legal move lists for a chess engine, bot, or reinforcement-learning environment
- Parsing and replaying PGN game archives for analysis tools or chess-study apps
Under The Hood
Architecture
chess.js centers on a single Chess class (src/chess.ts) that owns a flat 128-entry board array indexed with the classic 0x88 move-generation scheme — a bitwise trick (square & 0x88) that makes off-board detection a single AND operation, avoiding the bounds-checking overhead of a naive 8x8 array. Move generation, check detection, and game-state queries all operate directly against this array plus auxiliary state (castling rights, en passant square, move history), so the class is the whole engine: there’s no separate board/move-generator/game-state split, just one cohesive object whose public API (move(), moves(), isCheckmate(), fen(), load(), loadPgn()) is the entire surface area other code depends on.
Tech Stack
Written in TypeScript with zero runtime dependencies, chess.js uses Peggy (a PEG.js successor) to compile a .peggy grammar file into a PGN parser at build time, Rollup to produce CJS and ESM bundles, and the TypeScript compiler for type declarations. @microsoft/api-extractor tracks the public API surface to catch accidental breaking changes, and the package targets Node 20+ while also running in browsers.
Code Quality
The test suite (Vitest, 41 files under __tests__/) is organized one file per behavior — FEN symmetry, castling rights, checkmate/stalemate detection, PGN edge cases, glyphs, attackers — and exercises specific board positions rather than only happy-path cases. Invalid input surfaces as thrown Errors with descriptive messages (e.g. Invalid move: ${move}, Null move not allowed when in check) rather than silent failures. ESLint and Prettier run in CI (npm run check) alongside the Vitest suite across three Node versions, and TypeScript’s strict typing covers the public API end to end.
API Design
The library favors a small, memorable surface: one constructor, one move() method that accepts SAN strings or {from, to, promotion} objects, and boolean-returning predicate methods (isCheck(), isCheckmate(), isDraw()) that read like the chess terms they check. FEN and PGN round-trip through the same class with no separate serializer objects to wire up, and the zero-dependency footprint means adding it to a project requires no peer-dependency negotiation — a common friction point for board-UI libraries that need a rules engine underneath them.