solution-forest/filament-nestable-tree
Nestable drag-and-drop tree component for Filament v4/v5. Works with Eloquent (incl. kalnoy/nestedset) or static arrays, supports per-node actions, multi-tree pages, cross-tree drag-and-drop, lazy loading, and async child loading for large, complex trees.
Installation
composer require solution-forest/filament-nestable-tree
Publish the config (if needed):
php artisan vendor:publish --tag="filament-nestable-tree-config"
Basic Usage
For an Eloquent model (e.g., Category using kalnoy/nestedset):
use SolutionForest\FilamentNestableTree\Components\Tree;
Tree::make('Categories')
->model(\App\Models\Category::class)
->columns([
TextColumn::make('name'),
TextColumn::make('slug'),
])
->actions([
Action::make('edit')->url(fn ($record) => route('admin.categories.edit', $record)),
]);
First Use Case
Replace a Filament Table with a Tree for hierarchical data (e.g., categories, menus, or org charts). Test drag-and-drop reordering immediately.
Eloquent Integration
kalnoy/nestedset traits; no extra config needed.->treeColumn('parent_id') to specify a non-standard foreign key.->lazy() and define ->getChildrenQuery() for async child fetching.Static Data For non-database arrays (e.g., config menus):
Tree::make('Menu')
->records($menuItems) // Array of associative arrays
->recordIdPath('id') // Key to identify nodes
->recordParentPath('parent_id');
Multi-Tree Pages Combine multiple trees in a single page:
Tree::make('Primary Categories')
->model(\App\Models\Category::class)
->treeColumn('parent_id');
Tree::make('Secondary Categories')
->model(\App\Models\Category::class)
->treeColumn('alternate_parent_id');
Cross-Tree Drag-and-Drop Allow nodes from one tree to be dragged into another:
Tree::make('Tree A')
->crossTreeDragEnabled()
->crossTreeTargetTrees(['tree-b-slug']);
->actions([
Action::make('publish')
->visible(fn ($record) => $record->depth === 1),
]);
->recordTemplate() or ->emptyStateTemplate().->bulkActions().NestedSet vs. Closure Tables
NestedSet, but closure tables (e.g., laravel-nestedset’s alternative) require manual getChildrenQuery() overrides.->getChildrenQuery(function (Builder $query, $record) {
return $query->where('parent_id', $record->id);
});
Lazy Loading Performance
->getChildrenQuery() for lazy-loaded trees to optimize child fetching.->lazyDebounce(300) to reduce rapid-fire API calls during drag-and-drop.Cross-Tree Drag-and-Drop Quirks
parent_id column type).->crossTreeDragVisible() guards.State Persistence
useState() or a custom cookie handler for persistence:
->useState()
->statePath('tree_expanded_nodes');
config/filament-nestable-tree.php to log tree events:
'debug' => env('FILAMENT_NESTABLE_TREE_DEBUG', false),
filament-nestable-tree errors. Common fixes:
recordIdPath and recordParentPath match your data structure.kalnoy/nestedset is properly installed if using Eloquent.getChildrenQuery() with dd() to confirm it returns the expected query.Custom Tree Views Override the default Blade template by publishing assets:
php artisan vendor:publish --tag="filament-nestable-tree-views"
Then modify resources/views/vendor/filament-nestable-tree/....
Event Listeners
Listen for tree events (e.g., TreeNodeMoved) to trigger side effects:
event(new TreeNodeMoved($tree, $node, $newParent, $newPosition));
Styling
Extend SCSS variables in resources/css/filament-nestable-tree.scss:
@import "filament-nestable-tree/variables";
$tree-node-bg: #f0f0f0;
Testing
Use the TreeTestCase helper for unit tests:
use SolutionForest\FilamentNestableTree\Testing\TreeTestCase;
public function testTreeDragAndDrop()
{
$this->actingAs($user)
->tree('categories')
->dragNode($nodeId, $targetId)
->assertNodeMoved($nodeId, $newParentId);
}
How can I help you explore Laravel packages today?