A lightweight, customizable React component library for rendering and interacting with chess puzzles.
npm install @react-chess-tools/react-chess-puzzleA lightweight, customizable React component library for rendering and interacting with chess puzzles




- Overview
- Features
- Installation
- Quick Start
- Demo
- Puzzle Solving Flow
- API Reference
- ChessPuzzle.Root
- ChessPuzzle.Board
- ChessPuzzle.Reset
- ChessPuzzle.Hint
- Hooks
- useChessPuzzleContext
- useChessGameContext
- Integration with react-chess-game
- Examples
- License
@react-chess-tools/react-chess-puzzle is a React component library for creating interactive chess puzzle experiences. Built on top of @react-chess-tools/react-chess-game, it provides puzzle-specific features like move validation, hints, and progress tracking.
- Move Validation - Automatically validates moves against the puzzle solution
- Hints - Show the next correct move to help users
- Progress Tracking - Track puzzle state (not-started, in-progress, solved, failed)
- Callbacks - React to puzzle solve/fail events
- Built-in Reset - Easily restart puzzles or load new ones
- Sound Effects - Integrates with ChessGame.Sounds for audio feedback
- Keyboard Controls - Navigate through puzzle moves with keyboard
- TypeScript - Full TypeScript support with comprehensive type definitions
- Multiple solutions - Accept any checkmate as a solution (configurable via solveOnCheckmate)
``bash`
npm install @react-chess-tools/react-chess-puzzle
`bash`
yarn add @react-chess-tools/react-chess-puzzle
`bash`
pnpm add @react-chess-tools/react-chess-puzzle
`tsx
import { ChessPuzzle } from "@react-chess-tools/react-chess-puzzle";
function App() {
const puzzle = {
fen: "r1bqkbnr/pppp1ppp/2n5/4p3/4P3/5N2/PPPP1PPP/RNBQKB1R w KQkq - 2 3",
moves: ["d2d4", "e5d4", "f3d4"],
makeFirstMove: false,
};
return (
);
}
`
Visit the live demo to see the component in action.
1. Initial Setup - The board displays the position from the FEN string
2. First Move - If makeFirstMove is true, the component automatically plays the first move
3. User Interaction - The user attempts to solve the puzzle by making moves
4. Validation - Each move is validated against the solution:
- Correct move: The puzzle continues, opponent's response is auto-played
- Incorrect move: The puzzle is marked as failed
5. Completion - When all correct moves are made, the puzzle is marked as solved
The root component that provides puzzle context to all child components.
Note: This is a logic-only component (Context Provider). It does not render any DOM elements.
#### Props
| Name | Type | Default | Description |
| ------------------ | --------------------------------------- | ------- | ----------------------------------------------- |
| puzzle | Puzzle | - | The puzzle configuration (required) |onSolve
| | (ctx: ChessPuzzleContextType) => void | - | Callback when puzzle is solved |onFail
| | (ctx: ChessPuzzleContextType) => void | - | Callback when an incorrect move is made |solveOnCheckmate
| | boolean | true | When true, any checkmate move solves the puzzle |theme
| | PartialChessPuzzleTheme | - | Optional theme configuration |children
| | ReactNode | - | Child components |
#### Puzzle Object
| Property | Type | Default | Description |
| --------------- | ---------- | ------- | ------------------------------------------- |
| fen | string | - | Initial position in FEN notation |moves
| | string[] | - | Solution moves in algebraic or UCI notation |makeFirstMove
| | boolean | false | Whether to auto-play the first move |
#### Example
`tsx`
fen: "4kb1r/p2r1ppp/4qn2/1B2p1B1/4P3/1Q6/PPP2PPP/2KR4 w k - 0 1",
moves: ["Bxd7+", "Nxd7", "Qb8+", "Nxb8", "Rd8#"],
makeFirstMove: false,
}}
onSolve={(ctx) => console.log("Solved!", ctx.movesPlayed)}
onFail={(ctx) => console.log("Failed at move", ctx.movesPlayed)}
>
Renders the chess board. Delegates to ChessGame.Board under the hood.
Supports ref forwarding and all standard HTML div attributes (className, style, id, data-_, aria-_, etc.).
#### Props
| Name | Type | Description |
| ----------- | -------------------------------- | --------------------------------------------- |
| options | ChessboardOptions | Options forwarded to react-chessboard v5 |ref
| | Ref | Forwarded ref to the underlying board element |className
| | string | Custom CSS class names |style
| | CSSProperties | Custom inline styles |...
| | HTMLAttributes | All standard HTML div attributes |
#### Example
`tsx`
showNotation: true,
animationDurationInMs: 200,
}}
className="puzzle-board"
style={{ boxShadow: "0 4px 6px rgba(0,0,0,0.1)" }}
/>
A button component that resets the current puzzle or loads a new one.
Supports ref forwarding, asChild pattern, and all standard HTML button attributes (className, style, disabled, etc.).
#### Props
| Name | Type | Default | Description |
| ----------- | ----------------------------------------- | ----------------------------------- | --------------------------------------------------- |
| puzzle | Puzzle | - | New puzzle to load (resets current if not provided) |onReset
| | (ctx: ChessPuzzleContextType) => void | - | Callback after reset |showOn
| | Status[] | ["failed", "solved"] | States in which the button is visible |asChild
| | boolean | false | Render as child element (slot pattern) |ref
| | Ref | Forwarded ref to the button element |className
| | string | Custom CSS class names |...
| | ButtonHTMLAttributes | All standard HTML button attributes |
Status values: "not-started", "in-progress", "solved", "failed"
#### Example
`tsx`
#### Using asChild Pattern
`tsx
// Render as a custom button component
import { MyCustomButton } from './MyButton';
// Render as a link
e.preventDefault()}>
Restart Puzzle
`
#### Event Handler Composition
When using asChild, both your component's onClick and the Reset's onClick handler will work together:
`tsx`
// Both custom handler and reset logic will execute
A button that highlights the next correct move on the board.
Supports ref forwarding, asChild pattern, and all standard HTML button attributes (className, style, disabled, etc.).
#### Props
| Name | Type | Default | Description |
| ----------- | ----------------------------------------- | ----------------------------------- | -------------------------------------- |
| showOn | Status[] | ["not-started", "in-progress"] | States in which the button is visible |asChild
| | boolean | false | Render as child element (slot pattern) |ref
| | Ref | Forwarded ref to the button element |className
| | string | Custom CSS class names |...
| | ButtonHTMLAttributes | All standard HTML button attributes |
#### Example
`tsx
const puzzle = {
fen: "r1bqkbnr/pppp1ppp/2n5/4p3/4P3/5N2/PPPP1PPP/RNBQKB1R w KQkq - 2 3",
moves: ["d2d4", "e5d4", "f3d4"],
makeFirstMove: false,
};
`
#### Using asChild Pattern
`tsx`
Access the puzzle state and methods from any child component.
`tsx
import { useChessPuzzleContext } from "@react-chess-tools/react-chess-puzzle";
function PuzzleStatus() {
const { puzzleState, movesPlayed, totalMoves, resetPuzzle, onHint } =
useChessPuzzleContext();
return (
Status: {puzzleState}
Progress: {movesPlayed}/{totalMoves} moves
#### Return Values
| Property | Type | Description |
| -------------- | -------------------------- | -------------------------------------- |
|
status | Status | Current puzzle state |
| puzzleState | Status | Alias for status |
| movesPlayed | number | Number of correct moves made |
| totalMoves | number | Total moves in the solution |
| puzzle | Puzzle | The current puzzle object |
| hint | Hint | Current hint state |
| nextMove | string \| null | The next correct move |
| isPlayerTurn | boolean | Whether it's the player's turn to move |
| changePuzzle | (puzzle: Puzzle) => void | Load a new puzzle |
| resetPuzzle | () => void | Reset the current puzzle |
| onHint | () => void | Show hint for next move |$3
Since
react-chess-puzzle is built on react-chess-game, you can also access the underlying game context.`tsx
import { useChessGameContext } from "@react-chess-tools/react-chess-game";function BoardInfo() {
const { currentFen, info, methods } = useChessGameContext();
return (
Turn: {info.turn === "w" ? "White" : "Black"}
);
}
`Integration with react-chess-game
Since
react-chess-puzzle is built on react-chess-game, you can use any of its components:$3
`tsx
import { ChessPuzzle } from "@react-chess-tools/react-chess-puzzle";
import { ChessGame } from "@react-chess-tools/react-chess-game";function PuzzleWithSounds() {
const puzzle = {
fen: "r1bqkbnr/pppp1ppp/2n5/4p3/4P3/5N2/PPPP1PPP/RNBQKB1R w KQkq - 2 3",
moves: ["d2d4", "e5d4", "f3d4"],
};
return (
);
}
`$3
`tsx
import { ChessPuzzle } from "@react-chess-tools/react-chess-puzzle";
import { ChessGame } from "@react-chess-tools/react-chess-game";function PuzzleWithKeyboard() {
const puzzle = {
fen: "r1bqkbnr/pppp1ppp/2n5/4p3/4P3/5N2/PPPP1PPP/RNBQKB1R w KQkq - 2 3",
moves: ["d2d4", "e5d4", "f3d4"],
};
return (
);
}
`Examples
$3
`tsx
import { ChessPuzzle } from "@react-chess-tools/react-chess-puzzle";function BasicPuzzle() {
const puzzle = {
fen: "r1bqkbnr/pppp1ppp/2n5/4p3/4P3/5N2/PPPP1PPP/RNBQKB1R w KQkq - 2 3",
moves: ["d2d4", "e5d4", "f3d4"],
makeFirstMove: false,
};
return (
Restart
Hint
);
}
`$3
`tsx
import {
ChessPuzzle,
type ChessPuzzleContextType,
} from "@react-chess-tools/react-chess-puzzle";
import { useState } from "react";function PuzzleWithScore() {
const [score, setScore] = useState(0);
const handleSolve = (ctx: ChessPuzzleContextType) => {
setScore((prev) => prev + 10);
console.log(
Solved in ${ctx.movesPlayed} moves!);
}; const handleFail = (ctx: ChessPuzzleContextType) => {
setScore((prev) => Math.max(0, prev - 5));
console.log("Incorrect move!");
};
return (
Score: {score}
puzzle={puzzle}
onSolve={handleSolve}
onFail={handleFail}
>
Try Again
);
}
`$3
`tsx
import {
ChessPuzzle,
type ChessPuzzleContextType,
} from "@react-chess-tools/react-chess-puzzle";
import { ChessGame } from "@react-chess-tools/react-chess-game";
import { useState } from "react";const puzzles = [
{
fen: "4kb1r/p2r1ppp/4qn2/1B2p1B1/4P3/1Q6/PPP2PPP/2KR4 w k - 0 1",
moves: ["Bxd7+", "Nxd7", "Qb8+", "Nxb8", "Rd8#"],
makeFirstMove: false,
},
{
fen: "6k1/5p1p/p1q1p1p1/1pB1P3/1Pr3Pn/P4P1P/4Q3/3R2K1 b - - 0 31",
moves: ["h4f3", "e2f3", "c4c5", "d1d8", "g8g7", "f3f6"],
makeFirstMove: true,
},
];
function PuzzleTrainer() {
const [currentIndex, setCurrentIndex] = useState(0);
const [score, setScore] = useState(0);
const nextPuzzle = () => {
setCurrentIndex((prev) => (prev + 1) % puzzles.length);
};
const handleSolve = (ctx: ChessPuzzleContextType) => {
setScore((prev) => prev + 10);
nextPuzzle();
};
const handleFail = () => {
setScore((prev) => Math.max(0, prev - 5));
nextPuzzle();
};
return (
Score: {score} puzzle={puzzles[currentIndex]}
onSolve={handleSolve}
onFail={handleFail}
>
Restart
Hint
puzzle={puzzles[(currentIndex + 1) % puzzles.length]}
>
Skip
);
}
`$3
`tsx
import {
ChessPuzzle,
useChessPuzzleContext,
} from "@react-chess-tools/react-chess-puzzle";function PuzzleStatusDisplay() {
const { puzzleState, movesPlayed, totalMoves } = useChessPuzzleContext();
const messages = {
"not-started": "Make your move to start",
"in-progress":
Progress: ${movesPlayed}/${totalMoves} moves,
solved: "Puzzle solved! Well done!",
failed: "Incorrect move. Try again!",
}; return
status ${puzzleState}}>{messages[puzzleState]};
}function ResetLabel() {
const { puzzleState } = useChessPuzzleContext();
return puzzleState === "solved" ? "Next Puzzle" : "Try Again";
}
function PuzzleWithStatus() {
const puzzle = {
fen: "r1bqkbnr/pppp1ppp/2n5/4p3/4P3/5N2/PPPP1PPP/RNBQKB1R w KQkq - 2 3",
moves: ["d2d4", "e5d4", "f3d4"],
};
return (
);
}
``This project is MIT licensed.
Give a star if this project helped you!