HextaUI

Tree: Checkboxes

With checkboxes, checking a branch checks every enabled item inside it, and a partly checked branch shows a dash. Checking a branch also counts as checking children you load later. Clicking a row checks it, and the chevron opens it.

Projects
View projects
Edit projects
Delete projects
Members
Billing
View invoices
Change planOwner only

3 of 7 permissions granted

"use client"

import * as React from "react"

import { Tree, TreeGroup, TreeItem, TreeItemLabel } from "@/components/ui/tree"

const permissions = [
  {
    value: "projects",
    label: "Projects",
    children: [
      { value: "projects.view", label: "View projects" },
      { value: "projects.edit", label: "Edit projects" },
      { value: "projects.delete", label: "Delete projects" },
    ],
  },
  {
    value: "members",
    label: "Members",
    children: [
      { value: "members.invite", label: "Invite members" },
      { value: "members.remove", label: "Remove members" },
    ],
  },
  {
    value: "billing",
    label: "Billing",
    children: [
      { value: "billing.invoices", label: "View invoices" },
      { value: "billing.plan", label: "Change plan", disabled: true },
    ],
  },
]

const leaves = permissions.flatMap((group) => group.children)

export function TreeCheckboxes() {
  const [checked, setChecked] = React.useState<string[]>([
    "projects.view",
    "members.invite",
    "members.remove",
  ])
  const count = leaves.filter((leaf) => checked.includes(leaf.value)).length

  return (
    <div className="flex w-full max-w-xs flex-col gap-3">
      <Tree
        aria-label="Permissions"
        checkboxes
        checkedValues={checked}
        onCheckedValuesChange={setChecked}
        defaultExpandedValues={["projects", "billing"]}
      >
        {permissions.map((group) => (
          <TreeItem key={group.value} value={group.value}>
            <TreeItemLabel>{group.label}</TreeItemLabel>
            <TreeGroup>
              {group.children.map((permission) => (
                <TreeItem
                  key={permission.value}
                  value={permission.value}
                  disabled={"disabled" in permission}
                >
                  <TreeItemLabel
                    meta={"disabled" in permission ? "Owner only" : null}
                  >
                    {permission.label}
                  </TreeItemLabel>
                </TreeItem>
              ))}
            </TreeGroup>
          </TreeItem>
        ))}
      </Tree>
      <p className="text-sm text-muted-foreground" aria-live="polite">
        {count} of {leaves.length} permissions granted
      </p>
    </div>
  )
}

This adds the example as components/examples/tree/checkboxes.tsx, together with the Tree component and the HextaUI theme tokens it needs.

pnpm dlx shadcn@latest add https://hextaui.com/r/tree-checkboxes.json

To add only the component, follow the Tree installation.

Props in this example

The Tree props this example sets, from the API reference.

PropTypeDefault
defaultExpandedValues
string[]–
checkboxesShow cascading checkboxes. Rows are checked instead of selected.
booleanfalse
checkedValuesChecked items. A branch in the list counts as checking its children.
string[]–
onCheckedValuesChangeCalled with every checked leaf and every fully checked branch.
(values: string[]) => void–
PropTypeDefault
valueRequired. Unique within the tree.
string–
disabled
booleanfalse
PropTypeDefault
metaShown at the end of the row, such as a size, count or status.
ReactNode–

Every prop is in the Tree API reference.

See them all, with keyboard support and accessibility notes, on the Tree page.