npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-checktree

v1.1.6

Published

A lightweight, zero-dependency React tree menu component with hierarchical checkboxes, search, RTL/LTR support, and custom theming via CSS variables.

Readme

react-checktree

A lightweight, zero-dependency React tree component with hierarchical checkboxes, full-text search, RTL/LTR support, and CSS-variable theming.

npm version npm downloads bundle size license TypeScript

Live Demo · Storybook · npm · GitHub


Demo

Try it yourself — open the interactive Storybook to explore every feature with live controls.


Why react-checktree?

| | Feature | |---|---| | 0 deps | Zero runtime dependencies — just React as a peer | | Checkboxes | Tri-state checkboxes (checked / indeterminate / unchecked) with automatic parent-child reconciliation | | Search | Built-in full-text search with result highlighting and navigation | | RTL / LTR | First-class bidirectional layout support | | Theming | Fully customizable via a theme prop that maps to CSS variables | | i18n | All UI strings are overridable through the translation prop | | Actions | Built-in edit, copy, delete, and more buttons per node — or bring your own via renderNodeActions | | Field mapping | Use fieldNames to adapt any data shape — no need to transform your API response | | TypeScript | Written in TypeScript with full type exports | | Accessible | Proper ARIA roles (role="checkbox", aria-checked) and keyboard-friendly | | Lightweight | Small bundle, CSS-only animations, no heavy dependencies |


Quick Start

1. Install

react-checktree is not published on the npm registry yet. Commands like yarn add react-checktree or npm install react-checktree will fail with Not found / 404 until a release is published. Install from GitHub instead (below).

From GitHub (recommended for now). This runs the package prepare script so dist/ is built in node_modules:

npm install github:hoseinmohajer/react-checktree
yarn add github:hoseinmohajer/react-checktree
pnpm add github:hoseinmohajer/react-checktree

Pin a branch or commit with a # suffix, e.g. github:hoseinmohajer/react-checktree#main (see your package manager’s docs for git URLs).

From npm (after the package is published):

npm install react-checktree
# or
yarn add react-checktree
pnpm add react-checktree

2. Import the stylesheet

import "react-checktree/style.css";

3. Render

import { MenuTree } from "react-checktree";
import "react-checktree/style.css";

const nodes = [
  { id: "1", label: "Documents" },
  {
    id: "2",
    label: "Pictures",
    children: [
      { id: "2-1", label: "Vacation" },
      { id: "2-2", label: "Family" },
    ],
  },
  { id: "3", label: "Music" },
];

export default function App() {
  return <MenuTree nodes={nodes} title="File Browser" />;
}

That's it — you have a working tree in three lines of config.


Examples

Basic tree

A flat or nested list rendered with expand/collapse controls and folder/file icons.

<MenuTree nodes={nodes} title="My Tree" />

With checkboxes

Pass onNodeCheck to enable tri-state checkboxes. Parent nodes update automatically when children change.

const nodes = [
  {
    id: "1",
    label: "Frontend",
    checked: true,
    children: [
      { id: "1-1", label: "React", checked: true },
      { id: "1-2", label: "Vue", checked: false },
    ],
  },
  { id: "2", label: "Backend", checked: false },
];

<MenuTree
  nodes={nodes}
  title="Tech Stack"
  onNodeCheck={(node, checked) => {
    console.log(node.label, checked);
  }}
/>

With action buttons

Enable per-node action buttons by passing any combination of onNodeEdit, onNodeCopy, onNodeDelete, and onNodeMore. Buttons appear on hover.

<MenuTree
  nodes={nodes}
  title="Projects"
  onNodeEdit={(node) => console.log("edit", node)}
  onNodeCopy={(node) => console.log("copy", node)}
  onNodeDelete={(node) => console.log("delete", node)}
/>

Custom action widget

Replace the default action buttons with your own React element using renderNodeActions.

<MenuTree
  nodes={nodes}
  title="Custom Actions"
  onNodeCheck={(node, checked) => console.log(node.label, checked)}
  renderNodeActions={(node, state) => (
    <button onClick={() => alert(`Details for ${node.label}`)}>
      {state.isChecked ? "Active" : "Inactive"}
    </button>
  )}
/>

Select mode (no checkboxes)

Omit onNodeCheck and use onNodeSelect for single-node click handling.

<MenuTree
  nodes={nodes}
  title="Select a category"
  onNodeSelect={(node) => console.log("selected:", node)}
/>

RTL layout

Set direction="rtl" for right-to-left languages. Pair with translation for full localization.

<MenuTree
  nodes={[
    {
      id: "1",
      label: "مدیریت کاربران",
      children: [
        { id: "1-1", label: "لیست کاربران" },
        { id: "1-2", label: "نقش‌ها و دسترسی‌ها" },
      ],
    },
  ]}
  title="پنل مدیریت"
  direction="rtl"
  onNodeCheck={(node, checked) => console.log(node.label, checked)}
  translation={{
    result: "نتیجه",
    resultCount: "تعداد نتایج",
    close: "بستن",
    search: "جستجو",
    closeAll: "بستن همه",
    openAll: "باز کردن همه",
  }}
/>

Header-less & bare mode

Hide the header (search bar, expand/collapse controls) or remove the container styling for seamless embedding.

{/* No header */}
<MenuTree nodes={nodes} headerLess onNodeCheck={handleCheck} />

{/* No container background, shadow, or border-radius */}
<MenuTree nodes={nodes} title="Embedded" bare onNodeCheck={handleCheck} />

Field mapping

Your API returns title instead of label? Use fieldNames to map any data shape — no data transformation needed.

const apiData = [
  {
    key: "1",
    title: "Products",
    desc: "All product categories",
    badges: ["E-commerce"],
    ticked: true,
    items: [
      { key: "1-1", title: "Electronics", ticked: true },
      { key: "1-2", title: "Clothing", ticked: false },
    ],
  },
];

<MenuTree
  nodes={apiData}
  fieldNames={{
    id: "key",
    label: "title",
    description: "desc",
    tags: "badges",
    checked: "ticked",
    children: "items",
  }}
  title="Product Catalog"
  onNodeCheck={(node, checked) => console.log(node.label, checked)}
/>

API Reference

<MenuTree /> Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | nodes | TreeNode[] \| Record<string, unknown>[] | required | The tree data. Can be your own shape if you provide fieldNames. | | fieldNames | FieldNames | undefined | Maps your data keys to the expected tree structure. See Field Mapping. | | title | string | undefined | Header title. Hidden when headerLess is true. | | direction | "ltr" \| "rtl" | "rtl" | Layout direction. | | showIcon | boolean | true | Show folder/file icons next to nodes. | | loading | boolean | false | Show a skeleton placeholder while data is loading. | | headerLess | boolean | false | Hide the header (search, expand/collapse, title). | | bare | boolean | false | Remove container background, shadow, and border-radius. | | disabled | boolean | false | Disable all interactions (checkboxes, clicks). | | theme | TTheme | undefined | Custom theme object. See Theming. | | translation | TTranslate | undefined | Override all UI strings. See Translation. |

Callback Props

| Prop | Type | Description | |------|------|-------------| | onNodeCheck | (node: TreeNode, checked: boolean) => void | Called when a checkbox is toggled. Passing this prop enables checkboxes. | | onNodeSelect | (node: TreeNode) => void | Called when a node is clicked (only when checkboxes are disabled). | | onNodeEdit | (node: TreeNode) => void | Shows an edit button on each node. | | onNodeCopy | (node: TreeNode) => void | Shows a copy button on each node. | | onNodeDelete | (node: TreeNode) => void | Shows a delete button on each node. | | onNodeMore | (node: TreeNode) => void | Shows a "more" button on each node. | | renderNodeActions | (node: TreeNode, state: NodeState) => ReactElement | Replace the default action buttons with a custom widget. |

TreeNode

interface TreeNode {
  id: string;
  label: string;
  description?: string;
  tags?: string[];
  checked?: boolean;
  children?: TreeNode[];
}

FieldNames

interface FieldNames {
  id?: string;        // default: "id"
  label?: string;     // default: "label"
  description?: string; // default: "description"
  tags?: string;      // default: "tags"
  checked?: string;   // default: "checked"
  children?: string;  // default: "children"
}

NodeState

Provided to renderNodeActions for context about the current node.

interface NodeState {
  isExpanded: boolean;
  isChecked: boolean;
  hasChildren: boolean;
  level: number;
}

Theming

Customize the entire look by passing a theme object. Every value maps to a CSS custom property, so you can also override them directly in CSS.

const darkTheme = {
  name: "dark",
  colors: {
    white: "#0c1222",
    white_200: "#111a2e",
    white_300: "#1e3a5f",
    white_500: "#1e293b",
    black_100: "#1e3a5f",
    black_200: "#64748b",
    black_300: "#94a3b8",
    black_400: "#f1f5f9",
    primary: "#10b981",
    secondary_100: "#1a3550",
    tertiary: "#0ea5e9",
    warning: "#f59e0b",
  },
  fontSize: {
    h5: "14px",
    h6: "12px",
    h8: "11px",
  },
  fontWeight: {
    bold: 600,
    normal: 400,
    high: 500,
  },
};

<MenuTree nodes={nodes} title="Dark Mode" theme={darkTheme} />

| CSS Variable | Theme Path | Description | |---|---|---| | --rmt-white | colors.white | Main background | | --rmt-white-200 | colors.white_200 | Secondary background | | --rmt-white-300 | colors.white_300 | Tertiary background | | --rmt-white-500 | colors.white_500 | Border / divider | | --rmt-black-100 | colors.black_100 | Light text / lines | | --rmt-black-200 | colors.black_200 | Muted text | | --rmt-black-300 | colors.black_300 | Secondary text | | --rmt-black-400 | colors.black_400 | Primary text | | --rmt-primary | colors.primary | Primary accent | | --rmt-secondary-100 | colors.secondary_100 | Secondary accent | | --rmt-tertiary | colors.tertiary | Tertiary accent / highlights | | --rmt-warning | colors.warning | Warning / error | | --rmt-font-h5 | fontSize.h5 | Large text | | --rmt-font-h6 | fontSize.h6 | Normal text | | --rmt-font-h8 | fontSize.h8 | Small text | | --rmt-fw-bold | fontWeight.bold | Bold weight | | --rmt-fw-normal | fontWeight.normal | Normal weight | | --rmt-fw-high | fontWeight.high | Medium weight |


Translation / i18n

Override any UI string by passing a translation object. This covers header controls, search, tooltips, footer legends, and empty states.

<MenuTree
  nodes={nodes}
  title="Arborescence"
  translation={{
    result: "résultat",
    resultCount: "résultats",
    close: "Fermer",
    search: "Rechercher",
    searchPlaceholder: "Rechercher...",
    closeAll: "Tout fermer",
    openAll: "Tout ouvrir",
    selected: "Sélectionné",
    notSelected: "Non sélectionné",
    noResults: "Aucun résultat trouvé",
    noResultsHint: "Essayez un autre terme",
  }}
/>

| Key | Default | Where it appears | |-----|---------|-----------------| | result | "result" | Header result count | | resultCount | "result count" | Header result count | | close | "close" | Search close button tooltip | | search | "search" | Search icon tooltip | | searchPlaceholder | — | Search input placeholder | | closeAll | "close all" | Collapse all tooltip | | openAll | "open all" | Expand all tooltip | | expandAll | — | Expand all label | | collapseAll | — | Collapse all label | | settings | — | Settings label | | itemCount | — | Item count label | | selected | "Selected" | Footer legend | | notSelected | "Not selected" | Footer legend | | edit | "Edit" | Edit action tooltip | | copy | "Copy" | Copy action tooltip | | delete | "Delete" | Delete action tooltip | | more | "More" | More action tooltip | | noResults | "No results found" | Empty search state | | noResultsHint | "Try a different search term" | Empty search hint |


TypeScript

All types are exported for your convenience:

import type {
  TreeNode,
  TreeMenuProps,
  FieldNames,
  NodeState,
  TTheme,
  TTranslate,
  TDirection,
} from "react-checktree";

Migration from v1 (react-checkbox-menu-tree)

This package was previously published as react-checkbox-menu-tree (v1). Version 2.0 is a full rewrite with a cleaner API.

Step 1 — Install the new package

Uninstall v1, then install v2. react-checktree may not exist on npm yet — if install fails with Not found, use the GitHub line (same commands as in Quick Start → Install).

npm uninstall react-checkbox-menu-tree
npm install react-checktree

If that fails on the registry:

npm uninstall react-checkbox-menu-tree
npm install github:hoseinmohajer/react-checktree

With Yarn or pnpm, use yarn add react-checktree / pnpm add react-checktree, or yarn add github:hoseinmohajer/react-checktree / pnpm add github:hoseinmohajer/react-checktree respectively.

Step 2 — Update imports

// v1
import MenuTree from "react-checkbox-menu-tree";

// v2
import { MenuTree } from "react-checktree";
import "react-checktree/style.css";

Step 3 — Prop changes

| v1 Prop | v2 Equivalent | Notes | |---------|---------------|-------| | data | nodes | Renamed | | hasCheckBox | — | Pass onNodeCheck to enable checkboxes | | onClick | onNodeSelect | Renamed | | propertiesMapper | fieldNames | Renamed, uses label instead of title | | leftSideWidget | renderNodeActions | New signature: (node, state) => ReactElement | | theme | theme | Same structure, now maps to CSS variables | | translation | translation | Expanded with more keys |

Data shape changes

// v1 node
{ id: 1204, title: "node_1", checked: "FULL", parentId: null, children: [] }

// v2 node
{ id: "1", label: "node_1", checked: true, children: [] }

Key differences:

  • id is now a string
  • title is now label
  • checked is a boolean (not "FULL" / "HALF" / "NOT") — half-check states are computed automatically from children
  • parentId is no longer needed — the tree structure is defined by nesting

The old react-checkbox-menu-tree package will continue to work but is deprecated.


Donate

Help me to stack sats!

0xB4B2008f50E945fA28a99f2A650a9bF97C3d55eC


Contributing

Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.

git clone https://github.com/hoseinmohajer/react-checktree.git
cd react-checktree
pnpm install
pnpm dev          # dev server
pnpm storybook    # storybook
pnpm test         # tests
pnpm build        # production build

Join me in making react-checktree even better.


Contact

If you have any questions or suggestions, please reach out:

[email protected]


License

MIT — free for personal and commercial use.