Library / SDK
jakezatecky/react-checkbox-tree avatar
jakezatecky/react-checkbox-tree

react-checkbox-tree keeps checked and expanded state under your control

A simple and elegant checkbox tree for React.

731 stars218 forksJavaScriptMIT

At a glance

What is it?
jakezatecky/react-checkbox-tree is a React component that renders a hierarchical list with checkboxes, expansion arrows and parent propagation, built on Font Awesome icons and CSS shipped with the package. It is a controlled component, so the parent owns the checked and expanded arrays.
Who is it for?
react-checkbox-tree is a reasonable pick for a React app that needs a permission tree, a file picker or a settings panel where parents, children and expansion all have to agree, and it costs little because the component ships its own CSS and TypeScript types. It is the wrong choice for a server rendered page, for a design system that cannot take a Font Awesome dependency, or for anyone who wants an uncontrolled component with internal state.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 25 days ago.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A tree where every level checks its own children

What you get is one React component that renders a list of nodes, each with a checkbox, a label and an expansion arrow when it has children. The behaviour that matters is the propagation between levels. Checking a parent checks its descendants, checking every child checks the parent, and a partially checked parent renders in a half-checked state. None of that is application code you have to write.

The component is deliberately small. It does not fetch, it does not validate, and it does not model a form. It draws a tree and reports what you checked and what you expanded. That boundary is what makes it reusable across very different screens: a permissions panel, a set of nested documents, a filter sidebar.

The styling comes from the package rather than from your design system. Two files are published, a compiled stylesheet at `node_modules/react-checkbox-tree/lib/react-checkbox-tree.css` and the source SCSS at `node_modules/react-checkbox-tree/src/scss/react-checkbox-tree.scss`, and you either include one of them or hand the path to a CSS loader. Icons are a separate dependency, and by default the component expects Font Awesome 5 or 6 to be loaded in the browser.

The component is controlled, and that is the main design decision

`CheckboxTree` is a controlled component. It renders the `checked` and `expanded` arrays you pass in and reports changes through `onCheck` and `onExpand`, and you are expected to write those values back. The project documentation is explicit that you must update both properties whenever a change occurs.

That means a minimal usage looks like this:

jsx
import { useState } from 'react';
import CheckboxTree from 'react-checkbox-tree';
import 'react-checkbox-tree/lib/react-checkbox-tree.css';

const nodes = [{
  value: 'mars',
  label: 'Mars',
  children: [
    { value: 'phobos', label: 'Phobos' },
    { value: 'deimos', label: 'Deimos' },
  ],
}];

function Widget() {
  const [checked, setChecked] = useState([]);
  const [expanded, setExpanded] = useState([]);

and the render half continues:

jsx
  return (
    <CheckboxTree
      nodes={nodes}
      checked={checked}
      expanded={expanded}
      onCheck={(checked) => setChecked(checked)}
      onExpand={(expanded) => setExpanded(expanded)}
    />
  );
}

The controlled model is the right choice when the selection has to live somewhere else anyway: in a form library, in a URL, in server state shared across two views. The cost is that there is no uncontrolled mode. If you want a checkbox tree that manages its own state, you have to keep two arrays in your component and wire the handlers yourself, which is exactly what the minimal example shows.

One requirement catches people out. Every node object must carry a unique `value`, because the component serializes node values into the `checked` and `expanded` arrays, and the project attributes that serialization to performance optimizations.

checkModel decides what lands in the checked array

The `checkModel` property controls the shape of the data that comes back. With the default value of `'leaf'`, only leaf node values are stored in the `checked` array, so a partially checked parent contributes nothing. Set it to `'all'` and every checked node, parents included, is written to the array.

This is a small property with a large effect on the code around it. With `'leaf'`, a parent is checked when all of its descendants are, and you compute that state yourself from the leaves you receive. With `'all'`, the array is a complete picture of the tree, but the same parent will appear in the array every time you select a descendant, so any diffing or comparison logic downstream has to account for duplicates in intent.

The neighbouring properties fill in the rest of the behaviour. `checkKeys` lists the keyboard keys that toggle a node's check status and defaults to `[' ', 'Enter']`, so a keyboard user can already tick boxes without a mouse. `direction` switches the layout between `'ltr'` and `'rtl'`, which matters for right-to-left locales. `disabled` freezes checking, `expandDisabled` freezes expansion, and `expandOnClick` makes labels expand the node instead of only the arrow, with the requirement that a non-empty `onClick` function is supplied.

Icons are a Font Awesome dependency you can escape

By default the component renders Font Awesome 5 and 6 icons for check, uncheck, half check, expand, collapse, parent and leaf states. That means Font Awesome has to be present in the page, and it is the single largest reason the library can conflict with an existing design system.

There are two escape hatches. If you use Font Awesome 4, a single prop switches the icon set:

jsx
<CheckboxTree iconsClass="fa4" />

If you want no icon font at all, the `icons` property takes an object whose keys match the internal icon names, and each value is your own element:

jsx
<CheckboxTree
    ...
    icons={{
        check: <span className="rct-icon rct-icon-check" />,
        uncheck: <span className="rct-icon rct-icon-uncheck" />,
        halfCheck: <span className="rct-icon rct-icon-half-check" />,
        expandClose: <span className="rct-icon rct-icon-expand-close" />,
        expandOpen: <span className="rct-icon rct-icon-expand-open" />,
        expandAll: <span className="rct-icon rct-icon-expand-all" />,
        collapseAll: <span className="rct-icon rct-icon-collapse-all" />,
        parentClose: <span className="rct-icon rct-icon-parent-close" />,
        parentOpen: <span className="rct-icon rct-icon-parent-open" />,
        leaf: <span className="rct-icon rct-icon-leaf" />,
    }}
/>

The ten keys are the whole contract: check, uncheck, halfCheck, expandClose, expandOpen, expandAll, collapseAll, parentClose, parentOpen and leaf. You can override as many or as few as you like, and the project shows a variant where `react-fontawesome` components are passed directly, which is the path to take if you already render Font Awesome through React and want the tree to match.

Installing the package and pulling in its stylesheet

Installation is a single dependency, through whichever package manager you already use:

bash
yarn add react-checkbox-tree

or with npm:

bash
npm install react-checkbox-tree --save

The stylesheet has to be imported separately. The project shows the compiled CSS being pulled in directly from the package:

javascript
import 'react-checkbox-tree/lib/react-checkbox-tree.css';

Importing the CSS inside a JavaScript module is the convenient option because bundlers handle it, but it means the styles arrive as a side effect of the import rather than through your global stylesheet. If your build does not support that, include the file from your own CSS or point a CSS loader at the published paths instead.

The package ships TypeScript declarations from `src/index.d.ts` and declares both a CommonJS entry at `lib/index.cjs` and an ES module entry at `lib/index.js`, with an `exports` map that resolves either depending on how you import. The package also declares its own import aliases, `#js/*` and `#src/*`, for its internal modules.

A single JavaScript package with its own build pipeline

The repository is a plain JavaScript package rather than a TypeScript rewrite, with `src/` holding the component and `test/` holding the test suite run through Mocha, configured by `.mocharc.json`. Styling is handled by `stylelint` and `eslint` runs over the source, the examples and the tests.

The build is driven by `package.json` scripts and Webpack. Two Webpack targets are produced, a UMD bundle and an ES module bundle, and the stylesheet is compiled by Sass from `src/scss/react-checkbox-tree.scss` into `lib/react-checkbox-tree.css`, then run through PostCSS with autoprefixer. A separate Webpack config builds the examples, and `gh-deploy.sh` publishes the demo site to the project homepage at `jakezatecky.github.io/react-checkbox-tree`.

The release history is short and recent: v2.0.1 on 2026-04-18, v2.0.2 on 2026-05-28 and v2.1.0 on 2026-09-27. The repository is not archived and the last push was on 2026-09-28, one day after that release, while `package.json` already reports version 2.1.1, which suggests the branch carries commits made after the tagged release. The licence is MIT, so using it inside a commercial product carries no copyleft obligation and no per-seat fees.

Where the component stops being the right tool

The clearest case against it is a design system. If your application already has its own checkbox, its own disclosure icon and its own spacing rules, the `icons` prop makes this workable, but you are still shipping a component that renders its own markup and expects its own stylesheet to be loaded. Fighting that styling layer is more work than writing a small recursive tree yourself, which is the real alternative: a list of nodes with an `expanded` set and a `checked` set, roughly a hundred lines of your own React, with no icon dependency and no second set of CSS rules to maintain.

The other boundary is server rendering. The project documents no guidance on rendering to a string on the server, and the component is described entirely in terms of browser rendering, so if your application is a server rendered React app and the tree appears on first paint, you will have to check that yourself.

There is also a documentation gap. The property table published by the project ends mid-entry at `iconsClass`, so props beyond that point are not documented in what the project publishes. That is not a large risk for a component with this small an API, but it means the documented surface is the complete list you can rely on rather than a summary of one.

Editorial conclusion

react-checkbox-tree is a reasonable pick for a React app that needs a permission tree, a file picker or a settings panel where parents, children and expansion all have to agree, and it costs little because the component ships its own CSS and TypeScript types. It is the wrong choice for a server rendered page, for a design system that cannot take a Font Awesome dependency, or for anyone who wants an uncontrolled component with internal state. Before adopting it, check that the props table still matches your needs, because the documented list stops partway through the `iconsClass` entry, and note that the last push was on 2026-09-28 with v2.1.0 released on 2026-09-27 while package.json already reads 2.1.1.

Frequently asked questions

How do I install react-checkbox-tree?

Add it with `yarn add react-checkbox-tree` or `npm install react-checkbox-tree --save`. The stylesheet is not bundled, so you also import `react-checkbox-tree/lib/react-checkbox-tree.css` or include the published SCSS source in your own stylesheets.

Is react-checkbox-tree a controlled or an uncontrolled component?

It is controlled. You pass the `checked` and `expanded` arrays as properties and update them in the `onCheck` and `onExpand` handlers, as the minimal example in the project documentation does with state hooks.

How do I get rid of the Font Awesome icons in react-checkbox-tree?

Pass the `icons` property with your own elements for the icon names check, uncheck, halfCheck, expandClose, expandOpen, expandAll, collapseAll, parentClose, parentOpen and leaf. If you use Font Awesome 4, pass `iconsClass="fa4"` instead.

What does checkModel change in react-checkbox-tree?

With the default `'leaf'` value only leaf node values are stored in the `checked` array, while `'all'` stores every checked node including parents. The default is `'leaf'`.

What licence is react-checkbox-tree released under?

The package declares the MIT licence in package.json, so there is no copyleft requirement or per-seat fee for using it in a commercial product.

Official sources

  1. jakezatecky/react-checkbox-tree on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/jakezatecky-react-checkbox-tree.svg)](https://hysenlabs.com/projects/jakezatecky-react-checkbox-tree)

Was this useful?