Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Checkbox Tree Laravel Package

promethys/checkbox-tree

Render hierarchical checkbox trees in Laravel Blade with automatic parent-child selection, indeterminate states, and expandable nodes—ideal for permissions, categories, and nested filters.

View on GitHub
Deep Wiki
Context7

Getting Started

promethys/checkbox-tree (now tagged 1.0.0, the first stable release) is a form-oriented package: it renders a nested set of checkboxes from hierarchical data and gives you back the checked IDs on submit. The 1.0.0 tag itself ships with no changelog body beyond a commit link, so treat the tag as "API declared stable" rather than "features documented" — skim the commit range before assuming behaviour.

Minimal steps:

composer require promethys/checkbox-tree
php artisan vendor:publish --provider="Promethys\CheckboxTree\CheckboxTreeServiceProvider"

The publish step typically exposes the config (attribute names for id/label/children, whether parent nodes are selectable, asset registration) and the Blade views used to render each node. Read the published config first — nearly every behavioural question ("why is my parent checkbox disabled?", "why is the label column wrong?") is answered there rather than in the package source.

First use case to build: a category tree on an edit form. Load your adjacency-list model, build the nested array, hand it to the tree renderer with the model's currently-attached IDs, and validate the submitted array in a FormRequest. If that round-trip works, everything else is variation.

Where to look, in order:

  1. Published config/checkbox-tree.php — naming keys, asset paths, default view.
  2. The Blade view/component the package registers — that's your override point.
  3. The JavaScript shipped with the package — that's where parent/child cascading and indeterminate state live.

Implementation Patterns

Feeding the tree. Most usage builds a nested structure from an adjacency list (parent_id) or from a nested-set/closure-table package. Do the tree assembly yourself (or via ->toTree() on your model library) and pass the roots in; keep ordering explicit (orderBy('position')) before nesting, because the renderer preserves the order it receives and there is no sorting step in the view.

Checked state. Pass an array of IDs that should start checked. On submit, read back a flat array of IDs and sync:

$validated = $request->validate([
    'nodes'   => ['array'],
    'nodes.*' => ['integer', 'exists:categories,id'],
]);

$category->categories()->sync($validated['nodes'] ?? []);

Validate with exists — the client can post anything, including IDs from a tree the user was never shown.

Blade integration. The common pattern is a form partial that takes $tree, $checked, and a name prefix, rendered inside your existing form. If the package exposes a per-node view config key, override it once in config/checkbox-tree.php rather than copy-pasting markup into every form.

Per-node customization. For anything richer than label-plus-checkbox (icons, counts, "selected 3 of 7 descendants" badges), use the per-node view override and keep the input markup identical — the package's JS hooks the input by name/id, so changing those attributes silently breaks cascading.

Livewire / Inertia. With Livewire, bind the flat checked array to a public property and re-render the tree on change; make sure the tree data itself is a computed property, not rebuilt per request in the view. With Inertia, the package's Blade rendering doesn't apply — you'll be reimplementing the cascade in Vue/React, so consider it only if you also need the JS behaviour elsewhere.

Seeding and defaults. For "select all" defaults, expand parents server-side into descendant IDs and pass the full flat list as $checked; don't rely on the client to cascade a single parent ID downward.

Gotchas and Tips

  • Indeterminate state is client-side only. A parent showing a dash means "some children checked" in the DOM. It is not a submitted value. Recompute partial states server-side from the flat ID list if you need them for display after validation fails.
  • Re-render after validation errors. The checked set must come from old('nodes', $model->pluck('id')), not just the model, or the user loses their selection on a failed submit. This is the single most common complaint against tree-checkbox widgets.
  • Ordering is your job. Unsorted children produce a tree that looks shuffled and is hard to debug; sort before nesting, not after.
  • N+1 on deep trees. Eager-load the relation chain or build the tree from one flat query with get() + in-memory nesting. Recursive lazy loading on a 5-level tree is a measurable page-load hit.
  • Publish assets on upgrade. Version-bumped JS/CSS in public/vendor/... is not refreshed by composer update. Re-run vendor:publish --force (or your Vite pipeline) after any version change, then hard-refresh — otherwise you'll debug the old cascade logic.
  • Multiple trees on one page. Two instances in the same form need distinct name keys (nodes[] vs permissions[]) and, if the JS binds by selector, distinct container classes. Verify the config's selector option before duplicating a tree.
  • Debugging the input. dd($tree) immediately before the render call, and dd($request->all()) on submit. Nine times out of ten the bug is the array shape (children key name, or a mixed string/int ID set — in_array comparisons then fail because the checked list came back as strings from the request).
  • 1.0.0 specifically. There's no migration guide in the release notes. If you were on a 0.x/dev-main constraint, diff the published config and the views against your current copies before bumping — config key renames and view restructuring are the usual 1.0 breaking changes, and both fail quietly (tree renders empty, or checks don't restore) rather than throwing. Pin ^1.0 in composer.json and treat the next minor as potentially behaviour-changing until a real changelog appears.
  • Extension points worth knowing: the config file (attribute keys, selectable-parent flag, asset registration), the per-node view override, and — if the service provider binds a tree-builder class — container-swapping it for a custom builder is cleaner than post-processing the array in every controller.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core