Sortable
Row Reordering
Drag & drop row reordering with a toggle mode and automatic database persistence.
Drag & drop row reordering with a toggle mode and automatic database persistence.
Basic usage
use Livewire\Component;use NyonCode\WireTable\Table;use NyonCode\WireTable\Columns\TextColumn;use NyonCode\WireTable\Concerns\WithTable;use NyonCode\WireSortable\Concerns\WithSortable; class TaskTable extends Component{ use WithTable, WithSortable; public function table(Table $table): Table { return $table ->model(Task::class) ->reorderable() ->columns([ TextColumn::make('name', 'Name'), TextColumn::make('status', 'Status'), ]); } public function render() { return view('livewire.task-table'); }}
The WithSortable trait registers Table macros that add reorderable() and other methods to the base Table class. You chain them directly on $table.
The Blade template uses the computed $table property:
{{-- resources/views/livewire/task-table.blade.php --}}<div> {!! $this->table !!}</div>
How reorder mode works
- A "Reorder" button appears in the table toolbar
- User clicks the button to enter reorder mode
- In reorder mode:
- Drag handles appear on each row
- Pagination is disabled (all records are shown)
- Sorting, search, and filters are bypassed
- Rows are ordered by the sort column ascending
- User drags rows to their desired position
- On drag end, the new order is saved to the database
- User clicks "Done reordering" to exit reorder mode
- The table returns to its normal state with pagination, sorting, and filters restored
Custom order column
return $table ->model(Task::class) ->reorderable('position') ->columns([...]);
The column name must exist in your database table. Defaults to sort_order.
Always-on reorder mode
If you want drag handles visible at all times without a toggle button:
return $table ->model(Task::class) ->alwaysReorderable() ->columns([...]);
With a custom column:
return $table ->model(Task::class) ->alwaysReorderable('position') ->columns([...]);
In this mode the table is always in reorder mode -- no toggle button is rendered and $isReordering is set to true on mount.
Conditional reordering
Disable reordering based on a condition (e.g., user permissions):
return $table ->model(Task::class) ->reorderable('sort_order', auth()->user()->can('reorder', Task::class)) ->columns([...]);
When false is passed as the second argument, the reorder button does not appear and the toggleReordering() method is a no-op.
Paginated while reordering
By default, pagination is disabled in reorder mode so the user can drag across the full dataset. If you have a large dataset and prefer to keep pagination:
return $table ->model(Task::class) ->reorderable() ->paginatedWhileReordering() ->columns([...]);
Note: With pagination enabled, users can only reorder within the current page.
Lifecycle hooks
Override these methods in your component to hook into the reorder process:
protected function beforeReorder(array $items): void{ // Authorize, validate, or dispatch pre-reorder logic $this->authorize('reorder', Task::class);} protected function afterReorder(array $items): void{ // Clear cache, dispatch events, log activity Cache::forget('tasks.ordered'); $this->dispatch('tasks-reordered');}
Each $items entry is an associative array:
[ ['value' => '1', 'order' => 1], ['value' => '5', 'order' => 2], ['value' => '3', 'order' => 3],]
value-- the record's primary keyorder-- the new 1-based position
Custom primary key
By default, reorder queries use the table's primary key (id). If your model uses a different key:
return $table ->model(Task::class) ->primaryKey('uuid') ->reorderable() ->columns([...]);
Row Reordering Flow
- The table toolbar shows a reorder toggle when row reordering is enabled
- The user enters reorder mode
- The table displays records ordered by the configured order column
- The user drags rows into the desired order
- Wire Sortable receives the new order and updates the order column in one database transaction
- Your
beforeReorder()andafterReorder()hooks run around the save - The table refreshes and exits or stays in reorder mode based on the user's action