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

Filament Select Tree Laravel Package

codewithdennis/filament-select-tree

Add a dynamic select tree field to Laravel and Filament apps. Build interactive hierarchical selection dropdowns from BelongsTo or BelongsToMany relationships, or custom queries. Customize parent and child queries, and toggle strict null-parent root nodes.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit

This is not a platform dependency; it is a single Filament form-field class (SelectTree) that wraps the treeselectjs JavaScript widget in Livewire/Alpine glue. That framing drives the entire evaluation:

  • Gated on Filament. If the application is not built on Filament 3.x/4.x, the package has zero applicability. This is a binary architectural question, not a spectrum.
  • Presentation-layer only. It reads existing Eloquent relationships (BelongsTo, BelongsToMany) or an arbitrary array via getTreeUsing() and renders a hierarchical picker. It does not own schema, migrations, or persistence. That makes adoption cheap and reversal cheap — the defining characteristic from a risk standpoint.
  • Data-shape assumption. The default path requires a self-referencing adjacency list (parent_id-style column, configurable, or a distinct key pair via withKey('code')). There is no nested-set or closure-table support. If the domain already models hierarchies that way, the default path is frictionless; if not, getTreeUsing() is the escape hatch and you inherit responsibility for query efficiency.
  • Escape hatches are first-class. getTreeUsing() (array or closure), prepend/append, modifyQueryUsing/modifyChildQueryUsing, disabledOptions/hiddenOptions closures, and storeResults() mean the package rarely blocks a requirement. The cost is that complex behavior moves into application code, where it is your maintenance burden, not the maintainer's.

Integration feasibility

Feasible in an afternoon for a Filament-fluent engineer. Two steps: composer require codewithdennis/filament-select-tree:4.x, then php artisan filament:assets. Configuration is fluent and documented extensively in the README (which is unusually complete for a package of this size — ~30 documented methods, filter examples, and a custom-tree contract).

The non-trivial work is not installation; it is the three places the package's defaults can diverge from your domain semantics:

  1. Branch-node selection. By default only leaves are selectable; enableBranchNode() and isGroupedValue() change what gets persisted. Whether "parent" selections store a parent ID, all descendant IDs, or a sentinel is a data-contract decision that must be made explicitly before the first record is written.
  2. Filtered/tenant-scoped queries. The documented behavior — children whose parents were filtered out get promoted to root nodes to avoid empty result sets — is a sensible UX default but a genuine correctness hazard under multi-tenancy or authorization scoping. strictNullParentRootNodes() exists precisely for this, and its introduction/repair in v4.2.0–v4.2.2 shows the semantics were actively in flux.
  3. Selection mode. Inferred from relationship type unless overridden with multiple(false). Implicit behavior that depends on the model's declared relationship is easy to get wrong silently.

Technical risk — overall low-to-moderate, and concentrated in a few named areas rather than diffuse:

  • Version lockstep with Filament. v4.x targets Filament 4, v3.x targets Filament 3, and they are separate branches. A Filament major upgrade forces a package upgrade in the same change window. This is the single most predictable source of future work.
  • Asset pipeline. filament:assets is a deploy-time step. In containerized or immutable deployments with a cached/read-only filesystem, a missed or misordered step yields a broken field with no server-side error. This is an operational failure mode dressed as a technical one.
  • Recursion and cycles. Tree building assumes acyclic parent_id chains. A data-entry or import bug that creates a cycle is not defended against in the documented surface. Worth a validation rule at write time.
  • Maintainer concentration. Single primary maintainer, modest star count, recent release history dominated by dependency bumps and small targeted fixes (strict-null handling, SPA-mode rendering, a tree-key change that was reverted). The revert is a mild caution signal about API stability at the margins.
  • Mitigating factors: MIT license, small surface area, zero database footprint, and full source availability. Worst case, the field is replaced in days or forked outright. This is close to the minimum possible blast radius for a dependency.

Key questions to resolve before committing

  • Are we on Filament 3 or Filament 4, and what is our Filament major-upgrade cadence? (Determines which branch and how often this dependency churns.)
  • What is the realistic maximum node count per tree, and maximum depth? (Drives client-render viability and whether server-side search is required.)
  • Is the hierarchy tenant-scoped or permission-scoped, and who owns the convention that both parent and child queries are scoped consistently?
  • Do we need selectable branch nodes, and if so, what exactly is persisted?
  • Who owns the filament:assets step in the deploy pipeline, and is it verified?
  • Is the existing parent-child column named parent_id, or do we need withKey()?
  • If the package were abandoned tomorrow, what would replacement cost? (Answer is "low" — but confirm the team agrees.)

Integration Approach

Stack fit

Excellent fit for Laravel + Filament admin panels; no fit otherwise. The dependency chain is unremarkable — Composer for the PHP side, an npm-published JS library (treeselectjs) shipped as compiled assets. No queue workers, no additional services, no database objects, no runtime daemons. It sits entirely inside the Filament render path.

The package is a genuine UX upgrade over the alternatives for hierarchical selection: a flat Select with indented labels degrades badly past two levels, and a custom Livewire cascade of dependent selects multiplies round-trips. The tree widget solves search, multi-level drill-down, tag display, and keyboard/accessibility concerns in one field. That is the value proposition, and it is real but bounded.

Migration path

There is effectively no data migration — the field reads existing relations. Three realistic starting points:

  • From a flat Select on a self-referencing table: drop-in. Swap the field class, keep the relationship, verify selection-mode inference. Hours, not days.
  • From a custom Livewire tree/cascade: swap the field, but port the scoping and disabled/hidden logic into modifyQueryUsing/modifyChildQueryUsing/disabledOptions closures. Watch for the root-promotion default changing visible results.
  • From nested-set or closure-table modelling: do not use the relationship path. Use getTreeUsing() with an explicit array or cached closure. This is a supported but more bespoke integration; budget for it.

If the schema does not currently have a self-referencing parent column, adopting this package is a data-modelling decision, not a UI decision, and should be treated as such.

Compatibility

  • Pin the major explicitly: codewithdennis/filament-select-tree:4.x (Filament 4) or :3.x (Filament 3). Do not float the constraint across the Filament major boundary.
  • Livewire/Alpine interplay is the historically fragile seam — SPA-mode rendering issues were fixed in the 3.x line, and asset publishing failures manifest as silent UI breakage. Include a smoke test on the field in staging after every Filament or Livewire bump.
  • Soft deletes are supported but opt-in via withTrashed(); confirm the intended default for archived nodes rather than inheriting it.

Sequencing

  1. Spike (0.5 day). Install in a branch, wire one real resource against production-shaped data, and measure render on the largest realistic tree.
  2. Contract decision. Fix the persistence semantics: leaf-only vs branch-selectable, single vs multiple, sentinel values (parentNullValue(-1) if a sentinel is needed), and tenant/permission scoping rules.
  3. Deploy-pipeline change. Add and verify the filament:assets step in staging and production before the first release that references the field. Treat a missing asset build as a release blocker.
  4. Pilot. One or two resources, one release cycle, with a written fallback (native Select with flattened labels) if issues appear.
  5. Roll out field-by-field, not as a sweep. Each additional usage is a configuration surface, not a repetition.
  6. Guardrails. Add model-level validation against cyclic parent_id chains and a test asserting scoped trees never surface out-of-scope nodes.

Operational Impact

Maintenance

Low effort, predictable shape. The package's own release cadence is dependency-bump-driven with occasional targeted fixes, which means staying current is cheap but also means you should not expect upstream to fix your edge case quickly. Two recurring maintenance chores:

  • Filament major upgrades require a coordinated package version switch. Put the package on the same upgrade ticket as Filament; never upgrade one without the other.
  • The published asset bundle must be re-published when the package updates. A stale bundle against updated PHP classes is a subtle, hard-to-diagnose failure class.

Bus factor is the notable structural weakness: one primary maintainer, modest adoption, no dependents recorded. The offsetting factor is that the codebase is small, MIT-licensed, and the field is a thin wrapper — a fork is a day of work, not a quarter. Do not let this dependency accumulate deep application logic inside its callbacks; keep the closures thin so a fork or replacement stays cheap.

Support

Community-only: GitHub issues, no SLA, no vendor. Acceptable for an admin-panel widget on an internal tool; not acceptable if the field is on a revenue-critical or externally facing surface where a broken picker blocks transactions. Calibrate by surface, not by package quality.

Scaling

The meaningful constraint is client-side rendering of the full tree. The widget builds the entire node list in the DOM; defaultOpenLevel() and searchable() mitigate the UX cost but do not eliminate the payload. Practical guidance:

  • Under ~1,000 nodes with depth ≤ 4, expect no issues.
  • Beyond that, measure. If it degrades, use getTreeUsing() with a Cache::remember-backed array to remove per-render query cost, or scope trees per tenant/user so each instance is small.
  • withCount() adds aggregate work per render; storeResults() retains hydrated models in memory per component instance — both are fine in moderation and both are easy to sprinkle indiscriminately.
  • Search is client-side over the loaded tree. If the domain requires search across a dataset too large to render, this package is the wrong tool and a server-side autocomplete is the right answer.

Query cost is the more common real-world issue than DOM cost: naive parent_id traversal is one query per level per render. Cache the tree array and the concern largely disappears.

Failure modes

Failure Symptom Detection Mitigation
Assets not published / stale Field renders empty or broken, no server error Staging smoke test post-deploy Mandatory filament:assets in pipeline; assert on asset manifest
Filament major mismatch Class-not-found or Livewire render errors CI composer install + smoke test Pin :N.x to the Filament major; coupled upgrade tickets
Unscoped child query Cross-tenant options visible Authorization test on scoped trees Always pass modifyQueryUsing and modifyChildQueryUsing; use strictNullParentRootNodes()
Root-promotion surprise Filtered tree shows orphaned nodes at root Visual QA on filtered/filter-context views strictNullParentRootNodes() wherever scoping is active
Cyclic parent_id data Render hang or recursion error Model validation + import checks Prevent cycles at write time; do not rely on the field
Branch vs leaf ambiguity Wrong IDs persisted; downstream reports wrong Data assertions on saved records Fix persistence contract before pilot; add a test
Maintainer abandonment No upstream fixes Quarterly dependency review Keep closures thin; fork or replace is a day's work

Ramp-up

Genuinely small. A Filament-fluent engineer is productive in under an hour from the README alone; the documentation is above average for the ecosystem, with worked examples for relationships, custom queries, filters, and custom tree arrays. The learning cost is not in using the field but in deciding the three semantic questions (branch selectability, selection mode, scoping) and in debugging the Alpine/asset seam when something renders wrong — that latter task requires frontend literacy that the backend-most engineer on the team may not have.

Overall TPM judgement: a tactical, low-blast-radius UI dependency with a clear, bounded value proposition and a small, well-understood risk set. Adopt it if — and only if — the application is on Filament and the domain is already adjacency-list modelled. Do not let it become load-bearing, keep the configuration closures thin, and put asset publishing on the release checklist.

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.
gmostafa/php-graphql-client
xeviant/paystack
snowsoft/faker
meditationsteps/blogetc
animalyoung/laravel-influxdb
onix-solutions/sms-telera
lonely-lights/prosetta
oxygen/data
oxygen/core
jengo/base
xiloryx/log-collector
a21ns1g4ts/filament-stripe
la-php/laravel-admin
artisan-build/community-prompts
vleroy/smart
ayles-software/laravel-australian-public-holidays
cboxdk/laravel-id-spatie
cboxdk/laravel-id-billing
cboxdk/laravel-id-billing-bridge
simple-as-fuck/php-performance-log