mrcatz/datatable
Opinionated DataTable + CRUD framework for Laravel Livewire. Build admin pages fast with pagination, sorting, filtering, smart search, inline editing, bulk actions, expandable rows, exports, and a programmatic form builder. Includes artisan scaffolding; supports Tailwind + DaisyUI.
Installation
composer require mrcatz/datatable
Add the required directives to your base layout (app.blade.php):
@include('mrcatz::components.ui.notification')
@livewireScripts
@stack('scripts')
Tailwind Configuration
Update resources/css/app.css to include:
@source '../../vendor/mrcatz/**/*.blade.php';
Generate a CRUD Page Scaffold a new admin page with:
php artisan mrcatz:make Product --path=Admin
This creates a Livewire component with pre-configured columns, filters, and actions.
Add a Route
Route::get('/admin/products', \App\Livewire\Admin\Product\ProductPage::class);
ProductPage to display, filter, and edit products inline.createSearch, createCheck) to narrow down records.Column Definition Define columns in your Livewire component using chainable methods:
public function configureColumns(): array
{
return [
(new Column('name'))
->label('Product Name')
->sortable()
->searchable()
->editable(),
(new Column('price'))
->label('Price ($)')
->sortable()
->format(fn($value) => '$' . number_format($value, 2)),
];
}
Filter Integration Add filters to your table:
public function configureFilters(): array
{
return [
(new Filter('category_id'))
->label('Category')
->type('select')
->options(Product::categories())
->default('electronics'),
(new Filter('price'))
->label('Price Range')
->type('range')
->min(0)
->max(1000),
];
}
Inline Editing Enable inline editing for specific columns:
(new Column('status'))
->label('Status')
->editable()
->validationRule('required|in:active,inactive,paused')
->validationMessage('Status must be active, inactive, or paused.');
Bulk Actions Add bulk actions to the table toolbar:
public function configureBulkActions(): array
{
return [
(new BulkAction('delete'))
->label('Delete Selected')
->icon('trash')
->action(fn($ids) => Product::whereIn('id', $ids)->delete()),
];
}
Form Builder Use the form builder for add/edit modals:
public function configureForm(): FormBuilder
{
return (new FormBuilder())
->addSection('Basic Info', [
(new TextInput('name'))
->label('Product Name')
->required(),
(new NumberInput('price'))
->label('Price ($)')
->min(0),
])
->addSection('Advanced', [
(new SelectInput('category_id'))
->label('Category')
->options(Product::categories())
->required(),
]);
}
Export Integration Enable exports (CSV/Excel/PDF) with optional dependencies:
composer require maatwebsite/excel barryvdh/laravel-dompdf
Configure exports in your component:
use HasExport;
class ProductPage extends LivewireComponent implements HasExport
{
// ...
}
mount() to initialize data or updatedProperty() to react to changes.getDataQuery() to modify the base query:
protected function getDataQuery(): Builder
{
return Product::query()->where('is_active', true);
}
Filter Callbacks
// Correct
fn($query, $value) => $query->whereDate('created_at', $value),
// Incorrect (silently fails)
function ($query, $value) { $query->whereDate('created_at', $value); }
HasExport trait has its own filter application logic. Use the exportApplyBuilderCallback helper for consistency.Filter Height Mismatch
select filters, causing alignment issues.h-8 for inputs and buttons).Checkbox Alignment
createCheck filters may misalign checkboxes.items-start, leading-5, and mt-0.5 on checkboxes to maintain vertical alignment.Export Zero Rows
whereIn usage.buildExportQuery to handle check filters explicitly:
protected function buildExportQuery(): Builder
{
$query = $this->getDataQuery();
// Handle check filters separately
return $query;
}
Tailwind Scanning
@source for vendor blades may break styles.@source '../../vendor/mrcatz/**/*.blade.php';
\DB::enableQueryLog();
// Run your code
\DB::getQueryLog();
dd($this->data) or dump($this->filters) to inspect component state.Custom Columns
Extend the Column class to add custom logic:
class CustomColumn extends Column
{
public function customMethod()
{
return $this->modify(fn($value) => strtoupper($value));
}
}
Custom Filters
Create reusable filter types by extending Filter:
class CustomFilter extends Filter
{
public function apply(Builder $query): Builder
{
return $query->where('custom_field', $this->value);
}
}
Form Builder Extensions Add custom input types:
class CustomInput extends Input
{
public function render()
{
return '<input type="custom" value="' . $this->value . '">';
}
}
Export Customization
Override buildExportQuery or extend HasExport to add custom export logic:
protected function buildExportQuery(): Builder
{
$query = parent::buildExportQuery();
return $query->with(['relationship']);
}
Theming Overrides Override DaisyUI or Tailwind classes in your CSS:
/* Override filter button height */
.datatable-filter-button {
@apply h-8;
}
wire:model.live for real-time filtering or sorting.cursor() or chunk() in getDataQuery().$this->livewire(ProductPage::class)
->assertSee('Product Name')
->call('deleteSelected', [1, 2, 3]);
How can I help you explore Laravel packages today?