Chessboard.js integration

Chessboard.js
Chess Visualization library Integration

Chessboard.js integration

Chessboard.js provides the graphical chessboard interface for the site, supporting FEN positions, piece animations, drag-and-drop interaction, and responsive rendering. It is integrated with Chess.js, which manages the rules of the game, move legality, and PGN/FEN handling.

CSS for board display

The following <stylesheet> need to be at the top of the pages:


<!-- CSS Stylesheet -->
  <link rel="stylesheet" href="/assets/css/chess/chessboard-1.0.0.min.css" media="screen">

JS for board display

This library is requiring the full jquery to support animations.

The following <script> need to be placed near the end of the pages:


<!-- JavaScript Files -->
  <script src="/assets/js/chess/chessboard-1.0.0.min.js"></script>
  <script src="/assets/js/chess/chess-1.4.0.min.js"></script>

chess.js library is optional but can be used to validate valid positions.

Once the required CSS and JavaScript files have been included, both libraries are immediately available and can be used together. Chessboard.js is responsible for rendering and interacting with the graphical board, while Chess.js provides the game engine, allowing positions to be validated, legal moves to be enforced, and game state to be queried.

Creating a board requires only a placeholder element in the page:


<div id="myBoard" style="width: 400px; max-width: 100%;"></div>

The board is then instantiated from JavaScript by providing a configuration object. Typical callbacks handle user interaction, synchronize the displayed position with the game state, and update any information shown to the user. Since the underlying game is managed by Chess.js, every attempted move is validated automatically, while methods such as fen(), pgn(), turn(), isCheckmate(), and isDraw() provide convenient access to the current game state.

A minimal setup typically consists of callback functions for handling dropped pieces, refreshing the displayed position, updating the game status, and finally creating the board instance:


function onDrop(source, target) {
  // Validate and execute the move.
}

function onSnapEnd() {
  // Synchronize the displayed position.
}

function updateStatus() {
  // Refresh the status, FEN and PGN.
}

var config = {
  draggable: true,
  position: 'start',
  onDragStart: onDragStart,
  onDrop: onDrop,
  onSnapEnd: onSnapEnd
};

board = Chessboard('myBoard', config);

updateStatus();

The following example demonstrates a complete implementation that allows pieces to be moved with drag-and-drop, validates every move through Chess.js, keeps the board synchronized with the game state, and continuously updates the displayed status, FEN, and PGN.

Status:

Themes customization

As a small customization of chessboard.js, I created several board themes using different color palettes.

The implementation consists of wrapping the two classes which contains the color of the squares and the text (if notation is enabled):


.theme-custom .white-1e1d7 {
  background-color: #D0E1FD
  color: #1A8CFF;
}

.theme-custom .black-3c85d {
  background-color: #1A8CFF
  color: #D0E1FD;
}

The HTML becomes:


<div class="theme-custom">
    <div id="myBoard" style="width: 400px; max-width: 100%;"></div>
</div>

The first two use the primary and complementary colors of this website. The other two reproduce familiar chessboard color schemes inspired by Chess.com and Lichess.

To demonstrate the themes, I use four well-known positions from historical games. Each example includes the position, the side to move, and the main idea of the combination or plan.

The theme available on my website are the following:


default
primary
complementary
green
blue
wood
chesscom
chesscom-green
chesscom-blue
lichess-brown
lichess-blue

Ultimate king hunt

Edward Lasker vs. Sir George Alan Thomas, 1912

This position is an example of a direct king attack.

White starts with 11.Qxh7+!!, sacrificing the queen to force the black king away from its original position. The following moves illustrate how White controls the king's available squares and continues the attack as the king is forced across the board.

Theme: Primary

FEN: rn3rk1/pbppq1pp/1b2pb2/4N2Q/3PN3/3B4/PPP2PPP/R3K2R w KQ - 7 11

White to move

Immortal zugzwang game

Friedrich Samisch vs. Aron Nimzowitsch, 1923

This position demonstrates the idea of zugzwang and restriction.

Black's 25...h6!! leaves White with very few useful options. The position illustrates how controlling the opponent's pieces and available squares can become more important than making an immediate tactical threat.

Theme: Complementary

FEN: r5k1/pp2q1bp/2p1b1p1/3pP3/3P1Q2/2N5/PPB3PP/5RK1 b - - 0 25

Black to move

King of restraint

Anatoly Karpov vs. Wolfgang Unzicker, 1974

This position shows Karpov's approach to positional play.

Rather than immediately creating tactical complications, White gradually restricts Black's pieces and prevents counterplay. The plan involves improving the position of the pieces while keeping Black's position constrained. Moves such as 24.Ba7! contribute to this restriction and prepare the subsequent attack.

Theme: Chess.com

FEN: r1rq1bk1/1n1b1p1p/3p1np1/1p1Pp3/1Pp1P3/2P1BNNP/R2Q1PP1/1B2R1K1 w - - 0 24

White to move

Rubinstein's immortal game

Georg Rotlewi vs. Akiba Rubinstein, 1907

This position illustrates the importance of active pieces and tactical coordination.

Black plays 22...Rxc3!!, sacrificing the exchange to open lines against the white king. The bishops and queen then work together to create a mating attack. The combination is a useful example of how piece activity can compensate for material.

Theme: Lichess

FEN: 2rr2k1/1b3ppp/pb2p3/1p2P3/1P2BPnq/P1N3P1/1B2Q2P/R4R1K b - - 0 22

Black to move

These examples provide four different types of positions while also showing how the custom themes can be used with actual chess content.

Automatic load

The chessboard-render library provides automatic rendering of chessboards from HTML elements using data-* attributes. It supports both FEN positions and PGN games.

The following additional <script> need to be placed near the end of the pages.


<script src="/assets/js/chess/chessboard-render.min.js"></script>

The library automatically scans the page when the DOM is ready and renders:

  • .chessboard-fen-js elements as static FEN positions
  • .chessboard-pgn-js elements as navigable PGN games

No JavaScript initialization is required for individual boards.

FEN board

A FEN board can be created with:


<div id="myBoard6" class="chessboard-fen-js" data-orientation="black" data-file-name="fen-example-1" data-fen="r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1" data-theme="primary">
</div>

The data-fen attribute specifies the position to display. If it is omitted, the board starts from the standard initial position.

PGN board

A PGN board can be created with:


<div
  id="myBoard7"
  class="chessboard-pgn-js"
  data-orientation="white"
  data-theme="complementary"
  data-start-move="11"
  data-start-side="white"
  data-orientation="white"
  data-file-name="king-hunt"
  data-pgn='[Event "Edward Lasker vs Sir George Thomas"]
[Date "2020.11.09"]
[Result "1-0"]
[ECO "A83"]
[Opening "Dutch Defense: Staunton Gambit"]

1. d4 f5 2. e4 fxe4 3. Nc3 Nf6 4. Bg5 e6 5. Nxe4 Be7 6. Bxf6 Bxf6
7. Nf3 O-O 8. Bd3 b6 9. Ne5 Bb7 10. Qh5 Qe7 11. Qxh7+ Kxh7
12. Nxf6+ Kh6 13. Neg4+ Kg5 14. h4+ Kf4 15. g3+ Kf3 16. Be2+ Kg2
17. Rh2+ Kg1 18. Kd2#'>
</div>

The PGN is parsed by chess.js. The generated board starts at the initial position, followed by each position in the game. The navigation controls allow the user to move to the first position, previous move, next move, or final position. The rotate button changes the board orientation.

For PGNs containing a custom starting position, the standard PGN SetUp and FEN tags can be used:


[SetUp "1"]
[FEN "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"]

The FEN tag is then used as the initial position before replaying the moves.

Multiple PGN boards

Multiple PGN games defined directly in the HTML

Use chessboard-pgns-js when you want to display several games in the same chessboard. The games can be defined directly in the page using the data-pgns attribute.

Each entry contains a label, which is displayed in the game selector, and the complete PGN. The chessboard is created once and reused when the visitor switches between games.

For example:


<div
  class="chessboard-pgns-js"
  data-theme="primary"
  data-orientation="white"
  data-pgns='[
    {
      "startMove": 11,
      "startSide": "white",
      "orientation": "white",
      "fileName" : "king-hunt",
      "pgn": "[Event \"Casual game\"]\n[White \"Edward Lasker\"]\n[Black \"George Alan Thomas\"]\n\n1. d4 e6 2. Nf3 f5 3. Nc3 Nf6 ..."
    },
    {
      "label": "The Immortal Zugzwang Game",
      "startMove": 25,
      "startSide": "black",
      "orientation": "black",
      "fileName": "zugzwang",
      "pgn": "[Event \"Copenhagen\"]\n[White \"Friedrich Saemisch\"]\n[Black \"Aron Nimzowitsch\"]\n\n1. d4 Nf6 2. c4 e6 3. Nf3 b6 ..."
    }
  ]'>
</div>

When more than one game is provided, a dropdown is automatically displayed below the board, allowing the visitor to select a game.

Load a PGN collection from a JSON file

For larger pages or dynamically maintained content, the PGNs can be stored in a separate JSON file instead of embedding them directly in the HTML.

Use the data-pgn-json attribute to specify the URL of the JSON file:


<div
  class="chessboard-pgns-js"
  data-theme="blue"
  data-orientation="white"
  data-pgn-json="/files/chess/pgn/examples/automatic-load-single.min.json">
</div>

The JSON file contains the PGN data that will be loaded automatically when the page initializes. This keeps the HTML clean and allows the chess games to be maintained independently from the page.

The JSON format uses the same label and pgn structure as the inline data-pgns option.

If the JSON file contains only one game, no game selector is displayed.

The same remote JSON approach can be used when the file contains multiple games. This is useful for collections, annotated games, opening examples, historical games, or any other set of PGNs that should be presented through a single chessboard.


<div
  class="chessboard-pgns-js"
  data-theme="green"
  data-orientation="white"
  data-pgn-json="/files/chess/pgn/examples/automatic-load-multiple.min.json">
</div>

The library loads the JSON file asynchronously and initializes the chessboard once the games are available. When multiple games are returned, the game selector is automatically displayed below the board.

This approach also makes it possible to update or expand the collection without modifying the HTML containing the chessboard.

Square highlights

The chessboard visualizer supports square highlights that can be used with both FEN positions and PGN viewers.

Two highlight classes are available:

  • highlight-1 for the primary highlighted square
  • highlight-2 for the secondary highlighted square

The styling for each theme is defined with CSS:


.theme-default .highlight-1 {
  background-color: #FFFF00;
}

.theme-default .highlight-2 {
  background-color: #FFFFB3;
}

Highlights are supplied as JSON through the data-highlights attribute.

For a single position, the format is:


data-highlights='{
  "highlight1": ["h7"],
  "highlight2": ["h5"]
}'

This applies highlight-1 to h7 and highlight-2 to h5.

Internally, the visualizer clears the existing highlight classes before applying the new ones:


function setHighlights(board, highlights) {
  clearHighlights(board);

  if (!highlights) {
    return;
  }

  // Apply highlight-1 and highlight-2...
}

This means that changing the position does not leave highlights from the previous position behind.

For a FEN board, highlights can be added directly alongside the data-fen attribute.

For example, the position can specify:


data-highlights='{
  "highlight1": ["h7"],
  "highlight2": ["h5"]
}'

The FEN board therefore displays a single position with the requested squares highlighted.

This is useful when the purpose of the board is to draw attention to a particular tactical relationship, move, piece, or area of the position.

The same highlighting system can be used with a PGN viewer.

When data-highlight-moves="true" is used, the visualizer can automatically highlight the move being displayed. The destination square receives highlight-1 and the origin square receives highlight-2.

For example:


data-highlight-moves="true"

The PGN viewer obtains the move information while parsing the PGN:


moveData.push({
  from: move.from,
  to: move.to
});

It can then use that information when displaying a move:


setHighlights(board, {
  highlight1: [moves[currentMove].to],
  highlight2: [moves[currentMove].from]
});

This makes data-highlight-moves a convenient option when you simply want the currently played move to be visible on the board.

For more control, individual PGN positions can have their own manually defined highlights.

Instead of one pair of squares, the JSON is keyed by the move and side:


data-highlights='{
  "11w": {
    "highlight1": ["h7"],
    "highlight2": ["h5"]
  },
  "11b": {
    "highlight1": ["h7"],
    "highlight2": ["g8"]
  }
}'

Here:

  • 11w represents White's 11th move
  • 11b represents Black's 11th move
  • highlight1 is the destination/primary square
  • highlight2 is the origin/secondary square

The PGN viewer selects the appropriate highlight definition for the position currently being displayed.

This also means it can highlight something other than the actual move if it is necessary to use the board for annotation or explanation.

The dynamic PGN viewer supports the same highlight format when PGNs are loaded from JSON.

Each game in the PGN collection can have its own highlights property, alongside properties such as label, orientation, startMove, and pgn.

The JSON structure looks similar to:


[
  {
    "label": "Example game",
    "orientation": "white",
    "startMove": 11,
    "startSide": "white",
    "highlights": {
      "11w": {
        "highlight1": ["h7"],
        "highlight2": ["h5"]
      },
      "11b": {
        "highlight1": ["h7"],
        "highlight2": ["g8"]
      }
    },
    "pgn": "..."
  }
]

The highlights belong to a specific game, not to the viewer as a whole.

When a different game is loaded, its highlight data becomes the active set:


currentHighlights = gameConfig.highlights;

The normal showMove(move) function can then use the active highlights for the current position:


const highlights =
  getPgnHighlights(currentHighlights, currentMove);

This allows every game in a dynamic PGN collection to have completely different annotations.

FEN / PGN exports

The chessboard visualizer provides optional Download and Copy actions for FEN boards and PGN viewers.

The actions are enabled through the data-actions attribute. When enabled, the visualizer adds the action buttons directly below the chessboard.

  • Download exports the current board content as a text file.
  • Copy copies the current board content to the clipboard.
  • For PGN viewers, the exported content follows the currently selected game.
  • For PGN viewers with a mode switch, the switch determines whether FEN or PGN is exported.

Controls whether the Download and Copy buttons are displayed.

The action buttons are created by the createBoardActions() helper. The Download and Copy buttons are then created independently from the PGN navigation controls.

The Copy action uses ClipboardJS.

The Download action creates a plain-text Blob, generates a temporary object URL, and triggers a temporary anchor element:


downloadButton.on('click', function () {
  const mode = modeSwitch
    ? (modeSwitch.prop('checked') ? 'pgn' : 'fen')
    : extension;

  const content = getCopyText(mode);

  const fileName = config.fileName || crypto.randomUUID();
  const fileExtension = config.fileExtension || mode;

  const blob = new Blob([content], {
    type: 'text/plain;charset=utf-8'
  });

  const url = URL.createObjectURL(blob);

  const link = document.createElement('a');
  link.href = url;
  link.download = fileName + '.' + fileExtension;
  link.click();

  URL.revokeObjectURL(url);
});

This keeps the export entirely client-side. No server request is required to generate the downloaded file.

For a FEN board, the export callback returns the FEN assigned to the board:


createBoardActions(
  element,
  config,
  function () {
    return fen;
  },
  'fen'
);

Consequently, the Download action produces the FEN text and uses fen as the default file extension.

For a PGN viewer, the export callback returns the raw PGN of the selected game:


createBoardActions(
  element,
  config,
  function (mode) {
    if (mode === 'fen') {
      return positions[currentMove];
    }

    return pgns[currentGame].rawPgn;
  },
  'pgn'
);

The PGN export therefore uses rawPgn, rather than the normalized PGN used internally for move parsing. This preserves the original PGN supplied by the user, including headers, comments, and variations.

For multiple PGN viewers, filename and extension settings can be specified for individual games. The game configuration is resolved when the selected game changes. This allows each game to have its own download filename while retaining the board-level value as a fallback.

PGN viewers can optionally expose a mode switch between FEN and PGN export.

The switch is enabled with:


data-mode-switch="true"

When the PGN state is selected, Copy and Download operate on the original PGN.

When the FEN state is selected, they operate on the FEN corresponding to the position currently displayed by the viewer:


return positions[currentMove];

The displayed chessboard itself is not changed by switching export mode. The switch only changes the content supplied to the Copy and Download actions.

Data attributes

The following data-* attributes are available on FEN and PGN boards.

Attribute Values Default Description
data-renderer chessboard, merida chessboard Selects the renderer used to display the chessboard.
data-orientation white, black white Sets the initial board orientation.
data-coordinates true, false true Shows or hides board coordinates.
data-theme theme name default Selects the CSS theme applied to the board. For PGN boards, the theme also applies to the navigation controls and game selector.
data-small-board true, false false Displays the board using the small-board styling.
data-actions true, false false Displays the Download and Copy action buttons below the board.
data-mode-switch true, false false Displays a switch for toggling between PGN and FEN content in the Copy and Download actions.
data-file-name filename without extension generated UUID Specifies the base filename used when downloading the board content. If omitted, a random UUID is generated.
data-file-extension file extension without . fen or pgn Specifies the file extension used when downloading the board content. If omitted, the default extension for the board type is used.
data-start-move integer >= 0 0 For PGN boards, selects the move at which the viewer starts. 0 displays the initial position.
data-start-side white, black white For PGN boards, selects which side of the selected move is displayed. white means after White's move; black means after Black's move.
data-highlights JSON object none Defines manually selected square highlights. For FEN boards, use highlight1 and highlight2 directly. For PGN boards, highlights can be defined per move using keys such as 11w, 11b, 12w, etc.
data-highlight-moves true, false false Automatically highlights the origin and destination squares of the current PGN move. The destination uses highlight-1 and the origin uses highlight-2.
data-fen FEN string start Specifies the position for a FEN board.
data-pgn PGN string empty Specifies a single game for a PGN board.
data-pgns JSON array empty Specifies multiple PGN games directly in the HTML for a multiple-PGN board.
data-pgn-json URL empty Specifies a JSON file containing one or more PGN games for a multiple-PGN board.

data-fen is only used by .chessboard-fen-js, while data-pgn is only used by .chessboard-pgn-js and data-pgns and data-pgn-json are only used by .chessboard-pgns-js.

For .chessboard-pgns-js, data-pgns and data-pgn-json provide two alternative ways of supplying the games. The PGN itself remains the source of truth for game information, while the optional label is used only as the text displayed in the game selector.

When data-actions="true", the Download and Copy buttons are displayed. Copy copies the original FEN or PGN content. Download creates the file in the browser without requiring a server-side file.

For multiple-PGN boards, data-file-name and data-file-extension can also be specified per game in the JSON object. Per-game values override the corresponding board-level values.

For the multiple-PGN JSON, the corresponding optional properties are:


{
  "startMove": "11",
  "startSide": "black",
  "orientation": "black",
  "highlights": {...},
  "fileName": "Lasker-game-1",
  "fileExtension": "pgn"
}

Those JSON properties override the HTML data-* configuration for that individual game.

An id is optional, and if no id is provided, the library automatically generates a unique ID for the element before initializing chessboard.js.

Providing an ID is useful when the board needs to be referenced elsewhere.

The controls use Font Awesome icons and Bootstrap button classes.

The navigation styling can be customized through the btn-chessboard class and the selected theme:


.chessboard-controls {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 0.25rem;
  margin-top: 0.5rem;
}

.btn-chessboard {
  --bs-btn-padding-x: 0.75rem;
  --bs-btn-padding-y: 0.35rem;
}

.theme-primary .btn-chessboard {
  --bs-btn-bg: #0083B3;
  --bs-btn-border-color: #F8F8F2;
  --bs-btn-color: #F8F8F2;
  --bs-btn-hover-bg: #0083B3;
  --bs-btn-hover-border-color: #F8F8F2;
  --bs-btn-hover-color: #F8F8F2;
  --bs-btn-active-bg: #F8F8F2;
  --bs-btn-active-border-color: #0083B3;
  --bs-btn-active-color: #0083B3;
}

/* control for multiple pgn display */
.chessboard-pgn-selector {
  display: flex;
  justify-content: center;
  margin-top: 0.5rem;
}

The active state inverts the background, border, and text colors so that the button visibly changes while pressed.

No explicit JavaScript call is required. When the page loads, the library searches for the two supported classes:


$('.chessboard-fen-js').each(function () {
  createFenBoard(this);
});

$('.chessboard-pgn-js').each(function () {
  createPgnBoard(this);
});

$('.chessboard-pgns-js').each(function () {
  createPgnsBoard(this);
});

This means multiple boards can be placed on the same page simply by adding additional elements:


<div class="chessboard-fen-js" data-fen="start" data-theme="primary">
</div>

<div class="chessboard-fen-js" data-fen="8/8/8/8/8/8/8/K6k w - - 0 1" data-theme="wood">
</div>

<div class="chessboard-pgn-js" data-pgn='...' data-theme="complementary">
</div>

<div class="chessboard-pgns-js" data-pgn-json='...' data-theme="blue">
</div>

Each board is initialized independently.

Go to the top of the page