wsmallnews/filament-nestedset
Filament nestedset tree plugin built on kalnoy/nestedset, with multi-language and multi-tenancy support, unlimited nesting levels, tabbed switching, and a parent select field for elegant tree management.
Package: wsmallnews/filament-nestedset ^2.0 — MIT, 22 GitHub stars, 0 dependents, active releases (latest 2026-09-03), CI test + code-style workflows present.
The package is a presentation-layer plugin, not a domain library. It supplies a NestedsetPage base class, a make:filament-nestedset-page generator, and a standalone Livewire tree component that render against any Eloquent model using Kalnoy\Nestedset\NodeTrait. That is the right architectural shape for a TPM to approve: it does not own your data model, does not impose a repository or service layer, and does not touch your query builder beyond a getEloquentQuery() hook.
Composition is layered and legible:
| Layer | Owner | Coupling |
|---|---|---|
| Tree storage & move/insert algebra | kalnoy/nestedset (mature, widely used) |
Low — you own the schema via $table->nestedSet() |
| Parent picker field | codewithdennis/filament-select-tree |
Medium — transitive dependency, separate maintenance |
| Tree UI, pages, tabs, tenancy wiring | this package | Medium — you subclass NestedsetPage |
| UI shell, actions, forms, infolist | Filament v4/v5 | High — hard version coupling |
The extension surface is idiomatic Filament (schema(), createSchema()/editSchema(), infolistSchema(), static page properties, getRecordLabel() returning HtmlString). A Laravel team can onboard without reading package internals. That materially lowers integration cost.
Two architectural caveats:
_lft/_rgt across a contiguous range of rows. This is fine for navigation menus and category trees (hundreds of nodes, infrequent edits) and a poor fit for high-churn or high-concurrency hierarchies. This is a property of the chosen algorithm, not a defect in the plugin, but it is the single most important architectural constraint and it must be matched to the use case before adoption.kalnoy/nestedset's scoped-tree feature, driven by getScopeAttributes() on the model. Correctness of tenant isolation therefore depends on a convention split across two files (model + page) with no compile-time or runtime guarantee that they agree. That is a real correctness risk for a multi-tenant SaaS, and it is the thing I would insist on testing hardest.Feasible, with a short path. Concretely required work:
composer require wsmallnews/filament-nestedset:^2.0sn-filament-nestedset-config), optionally views and translationsNodeTrait to the model; add $table->nestedSet() to the migration (new or existing table)$recordTitleAttribute, form schema, navigation metadatagetScopeAttributes() to the model and the corresponding columns (team_id, type, etc.)That is roughly a one-to-two-day integration for a competent Filament developer on a greenfield table. The cost multiplier is (a) existing data needing backfill and (b) tenancy semantics.
Notable friction points I would flag before committing:
^4 || ^5 is uniformly green.autoload_assets => true globally injects CSS. Any project with a custom Filament theme must flip this off and import the stylesheet itself — an easy-to-miss step that produces a silently unstyled tree.filament-select-tree adds a second small-maintainer package to your supply chain for what is functionally one form field. Acceptable, but it belongs in the dependency-risk register.sn-filament-nestedset-leaf-click / node-click events, view/recordView overrides, getRecordUrl) is the right escape hatch when page-level abstraction is too coarse. It also means custom behavior can drift from the page component's behavior over time.| Risk | Severity | Likelihood | Mitigation |
|---|---|---|---|
| Bus factor — effectively one maintainer, 22 stars, 0 dependents | High | Medium | Vendor/fork under your org; MIT permits it. Budget ~1 dev-week/yr of maintenance headroom |
| Tenant scope misconfiguration leaks or cross-contaminates trees | High | Medium | Feature tests asserting scope isolation on move, create, delete; code review gate on getScopeAttributes() |
Tree corruption from writes outside NodeTrait (seeders, imports, raw SQL, admin tools) |
Medium | High | Enforce model-level writes only; run fixTree() in a scheduled integrity check until trust is established |
| Nested-set write amplification under concurrent drag-drop | Medium | Medium | Accept for low-churn trees; add DB transaction + row locking on move; do not use for >5k nodes or multi-editor hot paths |
| Filament v5 breaking changes | Medium | Medium | Pin minor versions; upgrade in a branch with the package's CI green first |
| Package abandonment mid-program | Medium | Low-Medium | Fork-ready posture from day one: no edits inside vendor/, all customization via subclassing |
The 23/100 composite score with 0 dependents is the honest signal here: this is unproven at scale, not defective. The code quality indicators that do exist (CI tests, code style workflow, changelog, releases, published config/views/translations, documented extension points, Laravel Boost guidelines) are all marks of a maintainer who intends the package to be used. That moves it from "avoid" to "adopt with guardrails" for the right use case.
getScopeAttributes() correctness becomes a security-relevant review item, not a config detail.fixTree() plan, and is there a rollback?autoload_assets)?Strong fit for a Filament v4/v5 admin panel on Laravel where the hierarchy is a content-organizing concern (navigation menus, category trees, org/department structures, CMS page trees) rather than a hot transactional workload. The abstraction level is right: you subclass a page, declare a form schema, and get drag-drop, reparenting, level limits, empty states, and per-row infolist for free. That is a genuine TPM win — the alternative is one to two sprints of bespoke Livewire tree work plus the ongoing cost of owning drag-drop UX.
Poor fit if any of the following hold: hierarchy is edited at high concurrency; node count is in the tens of thousands; the tree must be edited through an API rather than the panel; or the team is not on Filament at all.
Stack prerequisites to confirm in a spike: PHP and Laravel constraints from composer.json, Filament minor within the tested matrix, and kalnoy/nestedset compatible with the target Laravel version.
Greenfield table — the cheap path. Add $table->nestedSet() in the create migration, NodeTrait on the model, seed through the model (never raw inserts), done.
Existing table with parent_id only — the common real-world case:
_lft/_rgt columns via $table->nestedSet() in a separate migration.NodeTrait::fixTree(), chunked and logged._lft < _rgt, no overlapping ranges, parent_id consistent with range containment).parent_id-only code paths, ordering assumptions, or recursive loaders.Tenant-scoped migration — the highest-risk path:
team_id on existing rows before enabling scoping; unscoped rows in a scoped tree are a correctness bug, not a cosmetic one.fixTree() per scope value, not globally — scoped trees are independent range spaces.Rollback posture: keep the migration reversible and keep a pre-migration snapshot of the table. Tree backfills are recoverable only from backup in practice.
^2.0 targets Filament v4/v5; Filament v3 users must install the v1 branch. Confirm which branch matches our panel before evaluating further — the v1 branch is a different codebase and should be assessed separately.^2.x.y with a lockfile) and treat Filament major upgrades as a joint upgrade with this package.codewithdennis/filament-select-tree transitive dependency must be reviewed at the same time as the parent package; its Filament constraints are on the critical path.A four-phase sequence with a decision gate after phase 2:
| Phase | Scope | Exit criterion |
|---|---|---|
| 0 — Spike (2–3 days) | Throwaway branch; one model, one page, seeded ~500 nodes; exercise drag-drop, reparent, level limit, delete guards | Documented answer to node-count/concurrency questions; confirms Filament minor compatibility |
| 1 — Single-scope pilot | One real tree, non-tenant, e.g. admin navigation or a category set. No tabs, no tenancy | Ships to internal users; tree survives a week of real editing without corruption |
| 2 — Scoping | Enable tabs OR tenancy (not both at once). Feature tests for scope isolation | Isolation tests green under create/move/delete; integrity check clean |
| 3 — Breadth | Remaining trees; second scoping dimension; Livewire component reuse if needed | Fork-and-patch runbook documented; owner named |
Decision gate after phase 0/2: if the spike reveals node counts or concurrency in the "wrong algorithm" band, stop and evaluate a closure-table or materialized-path implementation instead. Better to spend three days finding that out than a quarter.
Deliberately do not do phases 1 and 2 simultaneously. Tabs and tenancy both route through getScopeAttributes(), and debugging a scoped-tree bug when two scope dimensions are live at once is materially harder than doing them in series.
The real maintenance question is not "does it work" but "who fixes it when it breaks at 2 a.m." With one effective maintainer and zero dependents, the honest answer is: we do. That is not disqualifying under MIT — but it has to be planned for, not discovered.
Recommended posture:
vendor/. All customization via subclassing NestedsetPage, overriding getRecordLabel(), getEloquentQuery(), nestedScoped(), schema(). This keeps the fork-and-patch option cheap.Community-only: GitHub issues, no vendor SLA, no commercial support tier, single-maintainer response cadence. Documentation is a thorough README plus a CHANGELOG — good for feature discovery, thin on troubleshooting, edge cases, and failure recovery. There is no separate docs site.
Practical consequence: the team's internal notes become the real documentation. Budget for a short internal runbook covering: how to add a tree, how scoping is wired in our codebase, how to run an integrity check, and how to recover a corrupted tree. That runbook is the mitigation for the support gap, and it is cheap to write during the pilot while the knowledge is fresh.
Scaling characteristics are inherited from kalnoy/nestedset, and they are asymmetric:
_lft BETWEEN x AND y), independent of depth. Render cost for the tree UI is the practical limit, not the query._lft/_rgt falls in the affected range. Moving a root-level node in a 2,000-node tree rewrites ~2,000 rows. That is a long transaction holding locks.| Failure | Symptom | Detection | Recovery |
|---|---|---|---|
| Tree corruption from out-of-band writes | Nodes missing from UI, wrong nesting, duplicate positions | Scheduled integrity assertion (_lft < _rgt, no overlap, parent containment) |
fixTree() per scope; restore from backup if severe |
| Cross-tenant scope leakage | Tenant A sees or moves Tenant B's nodes | Feature test on scope isolation; manual QA with two tenants | Immediate hotfix; audit getScopeAttributes() vs page config |
| Stale UI after concurrent edit | Drag-drop appears to succeed but reverts on reload | User reports | Locking on move; explicit refresh affordance; acceptable for internal tooling |
Delete guard confusion (allow_delete_parent / allow_delete_root) |
User cannot delete what they expect to delete, or deletes a subtree unintentionally | Config review at integration | Set both explicitly and document the intended semantics for each tree |
| Asset loading conflict with custom theme | Unstyled or double-styled tree | Visual QA | Set autoload_assets => false, import CSS in theme |
| Upstream incompatibility after Filament upgrade | Page class breaks, tree fails to render | CI + staging | Pin versions; upgrade jointly; fork if needed |
| Migration backfill failure on large existing table | Timeout, partial tree | Backfill command logging + row-count assertions | Idempotent re-runnable command; restore snapshot |
The two failures that actually hurt are silent tree corruption and scope leakage — both invisible until a user notices. Both are cheap to defend against with a scheduled integrity check and a scope-isolation test suite. Those two artifacts are the highest-value operational investment in this whole assessment and should be non-negotiable acceptance criteria for the pilot.
Low, and that is one of the package's genuine strengths.
fixTree(), and why writes are expensive takes longer than learning the API. That knowledge should be concentrated in one or two people, not spread thin.getScopeAttributes(). Pair on the first scoped tree.Overall TPM recommendation: adopt for internal Filament admin trees where node counts are modest and edit concurrency is low, conditional on (1) a successful 2–3 day spike confirming the algorithm fits the node-count/concurrency profile, (2) scope-isolation tests as a merge gate if multi-tenant, (3) a named maintainer with a documented fork-ready posture, and (4) a scheduled tree-integrity check. Do not adopt for high-concurrency hierarchies, very large trees, or any context where an unsupported single-maintainer dependency is unacceptable to the organization.
How can I help you explore Laravel packages today?