arkhas/livewire-datatable
Livewire Datatable
A powerful datatable component for Laravel Livewire with filters, actions, and export capabilities. Built with Flux Pro components.
Requirements
- PHP 8.1+
- Laravel 10.x or 11.x
- Livewire 3.x
- Flux Pro
Installation
composer require arkhas/livewire-datatable
Publish the config file (optional):
php artisan vendor:publish --tag=livewire-datatable-config
Publish the views for customization (optional):
php artisan vendor:publish --tag=livewire-datatable-views
Quick Start
1. Create a Datatable Component
php artisan make:datatable TasksTable
2. Configure Your Datatable
<?php
namespace App\Livewire;
use Livewire\Component;
use Arkhas\LivewireDatatable\Traits\WithDatatable;
use Arkhas\LivewireDatatable\Table\EloquentTable;
use Arkhas\LivewireDatatable\Columns\Column;
use Arkhas\LivewireDatatable\Columns\CheckboxColumn;
use Arkhas\LivewireDatatable\Columns\ActionColumn;
use Arkhas\LivewireDatatable\Filters\Filter;
use Arkhas\LivewireDatatable\Filters\FilterOption;
use Arkhas\LivewireDatatable\Filters\DateFilter;
use Arkhas\LivewireDatatable\Filters\RangeFilter;
use Arkhas\LivewireDatatable\Actions\TableAction;
use Arkhas\LivewireDatatable\Actions\ColumnAction;
use Arkhas\LivewireDatatable\Actions\ColumnActionGroup;
use App\Models\Task;
class TasksTable extends Component
{
use WithDatatable;
public function setup(): void
{
$table = new EloquentTable(Task::with('assignee'));
$table
->exportName('tasks-' . date('Y-m-d'))
->searchable(['title', 'description'])
->columns([
CheckboxColumn::make(),
Column::make('id')
->html(fn(Task $task) => (string) $task->id),
Column::make('title')
->label('Task')
->width('200px')
->toggable(false)
->html(fn(Task $task) => "<a href='/edit/{$task->id}'>{$task->title}</a>"),
Column::make('status')
->html(fn(Task $task) => ucfirst(str_replace('_', ' ', $task->status)))
->icon(fn(Task $task) => match($task->status) {
'todo' => 'circle',
'in_progress' => 'timer',
'done' => 'circle-check',
'canceled' => 'circle-x',
default => null,
}),
Column::make('priority')
->html(fn(Task $task) => ucfirst($task->priority))
->icon(fn(Task $task) => match($task->priority) {
'low' => 'arrow-down',
'medium' => 'arrow-right',
'high' => 'arrow-up',
default => null,
}),
Column::make('assignee.name')
->label('Assignee'),
ActionColumn::make()
->action(
ColumnActionGroup::make()
->icon('ellipsis-vertical')
->actions([
ColumnAction::make('edit')
->label('Edit')
->icon('pencil')
->url(fn(Task $task) => route('tasks.edit', $task->id))
->separator(),
ColumnAction::make('delete')
->label('Delete')
->icon('trash-2')
->confirm(fn(Task $task) => [
'title' => 'Delete Task',
'message' => "Are you sure you want to delete '{$task->title}'?",
'confirm' => 'Delete',
'cancel' => 'Cancel',
])
->handle(function (Task $task) {
$task->delete();
return ['success' => true, 'message' => 'Task deleted'];
}),
])
),
])
->filters([
Filter::make('status')
->multiple()
->label('Status')
->options([
FilterOption::make('todo')
->label('To Do')
->icon('circle')
->count(fn() => Task::where('status', 'todo')->count())
->query(fn($query, $keyword) => $query->where('status', $keyword)),
FilterOption::make('in_progress')
->label('In Progress')
->icon('timer')
->count(fn() => Task::where('status', 'in_progress')->count())
->query(fn($query, $keyword) => $query->where('status', $keyword)),
FilterOption::make('done')
->label('Done')
->icon('circle-check')
->count(fn() => Task::where('status', 'done')->count())
->query(fn($query, $keyword) => $query->where('status', $keyword)),
]),
Filter::make('priority')
->label('Priority')
->options([
FilterOption::make('low')->label('Low')->icon('arrow-down')
->query(fn($query, $keyword) => $query->where('priority', $keyword)),
FilterOption::make('medium')->label('Medium')->icon('arrow-right')
->query(fn($query, $keyword) => $query->where('priority', $keyword)),
FilterOption::make('high')->label('High')->icon('arrow-up')
->query(fn($query, $keyword) => $query->where('priority', $keyword)),
]),
// Example: Date filter (single mode)
DateFilter::make('created_at')
->label('Created At')
->column('created_at')
->min('2024-01-01')
->withToday()
->clearable(),
// Example: Date range filter
// RangeFilter::make('date_range')
// ->label('Date Range')
// ->column('created_at')
// ->withPresets()
// ->presets('today yesterday thisWeek last7Days thisMonth yearToDate allTime')
// ->minRange(3)
// ->maxRange(30),
])
->actions([
TableAction::make('delete')
->label('Delete')
->icon('trash-2', position: 'right')
->props(['variant' => 'destructive', 'size' => 'sm'])
->confirm(fn($ids) => [
'title' => 'Delete ' . count($ids) . ' Task(s)',
'message' => 'Are you sure you want to delete the selected tasks?',
'confirm' => 'Delete',
'cancel' => 'Cancel',
])
->handle(function ($ids) {
Task::whereIn('id', $ids)->delete();
return ['success' => true, 'message' => 'Tasks deleted'];
}),
]);
$this->table($table);
}
public function render()
{
return view('livewire-datatable::datatable', [
'table' => $this->getTable(),
'data' => $this->getData(),
]);
}
}
3. Use in Your Blade View
<livewire:tasks-table />
Features
Columns
- Column: Standard column with HTML rendering, icons, sorting, filtering
- CheckboxColumn: Row selection column
- ActionColumn: Actions dropdown menu per row
Filters
- Filter: Dropdown filter with multiple options and selection
- DateFilter: Date picker filter for single date selection (using Flux date picker)
- RangeFilter: Date picker filter for date range selection with presets support (using Flux date picker)
Column Options
Column::make('name')
->label('Display Label') // Custom label
->width('200px') // Column width
->sortable(true) // Enable sorting
->sortBy('custom_column') // Custom sort column
->toggable(true) // Can be hidden/shown
->hidden(false) // Initially hidden
->html(fn($model) => ...) // Custom HTML rendering
->icon(fn($model) => ...) // Dynamic icon
->filter(fn($query, $v) => ...) // Column-level filter
->exportAs(fn($model) => ...) // Custom export value
Filters
Dropdown Filters
Filter::make('status')
->label('Status')
->multiple() // Allow multiple selections
->options([
FilterOption::make('active')
->label('Active')
->icon('circle-check')
->count(fn() => Model::where('status', 'active')->count())
->query(fn($q, $keyword) => $q->where('status', $keyword)),
])
Date Filters
Date filters use the Flux date picker component. There are two types: DateFilter for single date selection and RangeFilter for date ranges.
Single Date Filter:
DateFilter::make('created_at')
->label('Created At')
->column('created_at') // Database column to filter on
->min('2024-01-01')
->max('today')
->withToday()
->selectableHeader()
->clearable()
Date Range Filter:
RangeFilter::make('date_range')
->label('Date Range')
->column('created_at')
->withPresets() // Enable preset date ranges (Last 7 Days, This Month, etc.)
->presets('today yesterday thisWeek last7Days thisMonth yearToDate allTime')
->minRange(3) // Minimum days in range
->maxRange(30) // Maximum days in range
->min('2024-01-01')
Common Options (available for both DateFilter and RangeFilter):
column(string)- Database column to filter on (defaults to filter name)min(string)- Minimum selectable datemax(string)- Maximum selectable datewithToday(bool)- Show "Today" shortcut buttonselectableHeader(bool)- Make month/year selectableclearable(bool)- Show clear buttondisabled(bool)- Disable the date pickerinvalid(bool)- Apply error stylinglocale(string)- Set the locale (e.g., 'fr', 'en-US')placeholder(string)- Placeholder textopenTo(string)- Date to open to if no date is selectedforceOpenTo(bool)- Force open to the open-to datemonths(int)- Number of months to displaystartDay(int)- Day of week to start on (0-6, Sunday-Saturday)weekNumbers(bool)- Display week numberswithInputs(bool)- Display date inputs at the topwithConfirmation(bool)- Require confirmation before applyingunavailable(string)- Comma-separated list of unavailable datesfixedWeeks(bool)- Display consistent number of weeks
RangeFilter Specific Options:
withPresets(bool)- Enable preset date rangespresets(string)- Space-separated list of presets to showminRange(int)- Minimum number of days in rangemaxRange(int)- Maximum number of days in range
Query Handling:
The filters automatically handle query building:
- DateFilter: Uses
whereDate()for exact date matching - RangeFilter: Uses
whereBetween()with start and end dates
You can also provide a custom query callback:
// For DateFilter
DateFilter::make('created_at')
->column('created_at')
->query(function ($query, $date) {
// Custom query logic
$query->whereDate('created_at', $date->format('Y-m-d'));
})
// For RangeFilter
RangeFilter::make('date_range')
->column('created_at')
->query(function ($query, $dates) {
// Custom query logic
[$startDate, $endDate] = $dates;
$query->whereBetween('created_at', [$startDate, $endDate]);
})
Actions
Bulk Actions (TableAction)
TableAction::make('delete')
->label('Delete Selected')
->icon('trash-2', position: 'right')
->props(['variant' => 'danger', 'size' => 'sm'])
->confirm(fn($ids) => [...])
->handle(fn($ids) => [...])
Row Actions (ColumnAction)
ColumnAction::make('edit')
->label('Edit')
->icon('pencil')
->url(fn($model) => route('edit', $model->id))
// OR
->handle(fn($model) => [...])
->confirm(fn($model) => [...])
->separator() // Add separator line after
Export
$table->exportName('filename')
->exportable(true)
->exportFormats(['csv', 'xlsx'])
Note: XLSX export requires
maatwebsite/excelpackage.
Configuration
// config/livewire-datatable.php
return [
'per_page' => 10,
'per_page_options' => [10, 25, 50, 100],
'default_sort_direction' => 'asc',
'search_debounce' => 300,
'export' => [
'default_format' => 'csv',
'formats' => ['csv', 'xlsx'],
'chunk_size' => 1000,
],
];
Customizing Views
Publish the views:
php artisan vendor:publish --tag=livewire-datatable-views
Views will be published to resources/views/vendor/livewire-datatable/.
Icons
This package uses Lucide icons via Flux. Icon names must match Lucide’s kebab-case names (e.g. circle-check, trash-2, arrow-down).
Install all icons and add the Tailwind @source for package views:
php artisan livewire-datatable:install
Icons Used in This Package
The following Lucide icons are used:
Column Icons:
circle- Default/empty statetimer- In progress statuscircle-check- Completed/done statuscircle-x- Canceled/removed statusarrow-down- Low priorityarrow-right- Medium priorityarrow-up- High priority
Action Icons:
ellipsis-vertical- Action menu triggerpencil- Edit actiontrash-2- Delete action
Filter Icons:
circle-plus- Add filter buttoncheck- Selected filter option
Toolbar Icons:
x- Reset filters buttondownload- Export buttonsliders-horizontal- View options buttonsearch- Search columns (magnifying glass)
Pagination Icons:
chevron-left- Previous pagechevron-right- Next pagechevrons-left- First pagechevrons-right- Last page
Sorting Icons:
arrow-up- Ascending sortarrow-down- Descending sortchevrons-up-down- Sortable indicator
You can use any Lucide icon by importing it with php artisan flux:icon <icon-name> and using it in your datatable configuration.
License
MIT License