yajra/laravel-datatables
Complete Laravel DataTables installer (core + plugins) for Laravel 13/PHP 8.3+. Integrates DataTables 2.x with Editor, Buttons, and Select extensions, providing a full server-side DataTables setup for Laravel apps.
Installation:
composer require yajra/laravel-datatables:^13
Publish config and assets (optional but recommended for customization):
php artisan vendor:publish --provider="Yajra\DataTables\DataTablesServiceProvider"
Register Service Providers (Laravel 5.5+):
Add to config/app.php:
Yajra\DataTables\DataTablesServiceProvider::class,
Yajra\DataTables\ButtonsServiceProvider::class,
Yajra\DataTables\FractalServiceProvider::class
First Use Case: Create a route and controller method to return a DataTable response:
// routes/web.php
Route::get('users/datatable', [UserController::class, 'datatable']);
// app/Http/Controllers/UserController.php
public function datatable()
{
return DataTables::of(User::query())
->addColumn('action', function($user) {
return '<button class="edit-btn" data-id="'.$user->id.'">Edit</button>';
})
->make(true);
}
Frontend Integration: Include DataTables CSS/JS in your Blade view:
<link rel="stylesheet" href="https://cdn.datatables.net/2.0.3/css/dataTables.dataTables.css">
<script src="https://cdn.datatables.net/2.0.3/js/dataTables.js"></script>
<table id="users-table" class="display">
<thead>
<tr>
<th>Name</th>
<th>Email</th>
<th>Actions</th>
</tr>
</thead>
</table>
<script>
$(document).ready(function() {
$('#users-table').DataTable({
processing: true,
serverSide: true,
ajax: '{{ route("users.datatable") }}',
columns: [
{ data: 'name', name: 'name' },
{ data: 'email', name: 'email' },
{ data: 'action', name: 'action', orderable: false, searchable: false }
]
});
});
</script>
tests directory in the package repository for real-world usage patterns.config/datatables.php for customizing default behaviors (e.g., response formatting, query builder settings).// Simple DataTable with Eloquent model
return DataTables::of(User::query())
->addIndexColumn() // Adds a serial number column
->editColumn('name', 'name_edit') // Custom edit column
->make(true);
// For non-Eloquent queries (e.g., raw SQL or complex joins)
return DataTables::of(
DB::table('users')
->join('roles', 'users.role_id', '=', 'roles.id')
->select('users.*', 'roles.name as role_name')
)
->addColumn('full_role', function($user) {
return $user->name . ' (' . $user->role_name . ')';
})
->make(true);
// For non-database collections (e.g., API responses, cached data)
$collection = Cache::get('expensive_data', function() {
return collect(range(1, 1000))->map(fn($i) => ['id' => $i, 'value' => 'Item ' . $i]);
});
return DataTables::of($collection)
->make(true);
// Override default server-side processing
return DataTables::of(User::query())
->setTransformer(function($query) {
// Customize the query before processing
return $query->where('active', true);
})
->make(true);
Livewire:
public function datatable()
{
return DataTables::of(User::query())
->addColumn('action', 'users.action')
->make(true);
}
In Livewire component:
<livewire:datatable wire:model="users" />
Use yajra/laravel-livewire-datatables for seamless integration.
Inertia.js: Return JSON response from Laravel and process in Vue/React:
return DataTables::of(User::query())
->make(true, true); // Returns raw array for Inertia
return DataTables::of(User::query())
->addColumn('action', 'users.action')
->addActionColumn(['edit' => 'users.edit', 'delete' => 'users.delete'])
->editColumn('name', 'name_edit')
->exportOptions([
'format' => 'xlsx',
'filename' => 'users_export',
'buttons' => [
'excel', 'csv', 'pdf', 'print'
]
])
->make(true);
return DataTables::of(User::query())
->selectInput()
->make(true);
Frontend:
$('#users-table').DataTable({
select: true,
// ... other options
});
return DataTables::of(User::query())
->editColumn('name', 'name_edit')
->editColumn('email', 'email_edit')
->make(true);
Frontend:
$('#users-table').DataTable({
dom: 'Bfrtip',
buttons: [
'create', 'edit', 'remove'
],
// ... other options
});
Override default JSON response:
return DataTables::of(User::query())
->make(true, true); // Returns raw array
Then format as needed:
return response()->json([
'data' => $data,
'recordsTotal' => $recordsTotal,
'recordsFiltered' => $recordsFiltered,
'draw' => $draw,
// Custom fields
'custom_field' => 'value'
]);
Selective Column Loading:
return DataTables::of(User::query())
->select(['id', 'name', 'email']) // Only load these columns
->make(true);
Lazy Loading:
return DataTables::of(User::query())
->lazyLoad(function($query) {
// Apply filters dynamically
if (request('search')) {
$query->where('name', 'like', '%' . request('search') . '%');
}
})
->make(true);
Caching:
return DataTables::of(Cache::remember('users_datatable_' . request()->getContent(), 60, function() {
return User::query();
}))
->make(true);
CSRF Token Mismatch:
VerifyCsrfToken middleware:
protected $except = [
'users/datatable',
// ... other DataTables routes
];
meta option in DataTables:
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
Column Name Mismatch:
name in columns:
columns: [
{ data: 'name', name: 'name' }, // Ensure 'name' matches backend
{ data: 'email', name: 'email' }
]
Pagination Conflicts:
return DataTables::of(User::query()->simplePaginate(10))
->make(true);
Or use ->paginate() with length parameter:
$.fn.dataTable.ext.pager.numbers_length = 5; // Show 5 page buttons
Memory Leaks with Large Datasets:
How can I help you explore Laravel packages today?