Data tables · Open source
Laravel Grid.js
Turn any Eloquent query into a searchable, sortable, paginated Grid.js data table with one fluent PHP call.
What it solves
A data table over a large Eloquent query needs search, sort and pagination on the server, plus a data endpoint that must not let the browser choose which model or columns to read.
Writing that route, controller and JavaScript for every table is repetitive and easy to get wrong.
You build the grid from your own query in PHP and echo it in Blade. Grid.js fetches one page at a time from a built-in endpoint that only runs the query and columns sealed in an encrypted token, with no route, controller or JavaScript to write.
- Keeps your query: where clauses, scopes, joins, eager loads and default order
- Search (LIKE, relation columns included), sort and LIMIT/OFFSET pagination run in the database
- Encrypted grid token, so the data endpoint never takes a model or column name from the browser
- Built-in date, datetime, number, currency, boolean and link formats, all escaped
- Action buttons with {id} URL placeholders; link formatters refuse javascript: URLs
- Grid.js 6.2.0 pinned on jsDelivr with SRI, or served from your public/ folder
Install
Requires PHP 8.3+ and Laravel 12 and 13.
composer require amjadiqbal/laravel-gridjs
Usage
use AmjadIqbal\GridJS\Facades\Grid;
$customers = Grid::fromQuery(Customer::active()->with('team'))
->columns(['name', 'email', ['field' => 'team.name', 'name' => 'Team']])
->searchable()
->sortable()
->pagination(10);
return view('customers.index', ['customers' => $customers]);
// In the view: {{ $customers }}
Setup
The service provider, the Grid facade and the data route register themselves. Optionally publish the config and choose CDN or local assets, or copy Grid.js into public/vendor/gridjs and set GRIDJS_CDN=false.
php artisan gridjs:install
php artisan gridjs:publish-assets
Columns
Per column: field, name, width, hidden, searchable, sortable, format and formatter. Accessors and casts work in cells but cannot be searched or sorted in SQL, so switch those off for them. Relation columns are searchable through whereHas but not sortable.
->columns([
'name',
'team.name',
['field' => 'email', 'name' => 'E-mail', 'width' => '240px'],
['field' => 'full_name', 'searchable' => false, 'sortable' => false],
['field' => 'mrr', 'name' => 'MRR', 'format' => ['type' => 'currency', 'currency' => 'USD']],
])
Search, sort, pagination and actions
The browser can only pick a page, a keyword and one of the sortable fields. A grid without pagination() returns at most gridjs.max_rows rows (1000). {id} in an action URL is filled from the key field.
->searchable(['name', 'email'], 200) // only these, 200 ms debounce
->sortable(['name', 'created_at'])
->pagination(25)
->actions([['label' => 'Edit', 'url' => '/customers/{id}/edit']])
Protecting the data
Every grid fetches from GET /gridjs/data. Add auth (or any middleware) in config/gridjs.php to keep it behind login. Tokens are encrypted with your APP_KEY, so rotating the key invalidates grids that are already open. Tampered or missing tokens get a 403. A grid token works for anyone who holds it until APP_KEY changes, so put the endpoint behind auth and scope the query itself to the current user. A hidden column is only hidden in the table: its values are still in the JSON response, so leave private columns out of the query.
// config/gridjs.php
'middleware' => ['web', 'auth'],
Assets and styling
- The first grid on a page prints the Grid.js script and theme plus a small mount script, once per request.
- Add @gridjsAssets to your layout's <head> to print them there, or set gridjs.assets.inject to false and load Grid.js yourself.
- With Vite::useCspNonce() the inline scripts get the nonce.
- ->theme('mermaid') is the default; ->theme(null) and ->className([...]) let you style it with Tailwind or your own CSS.
- Custom JavaScript formatters run as-is, so never build them from user input.
Hire me
Need this built into your app, or something custom?
I wrote Laravel Grid.js. I can wire it into your Laravel app, extend it for your case, or build the feature around it. Tell me what you need and you'll get a plan and a quote, free.
Support
Found a bug or want a feature? Open an issue on GitHub. For help adding it to your own project, use the quote form above.
More Laravel packages
All packages
Laravel Driver.js
Build Driver.js product tours in PHP and render them with one Blade component.
Laravel Sonner
Sonner toast notifications for Laravel, sent from PHP and shown without React.
Laravel Ninja Keys
A Cmd+K / Ctrl+K command palette for Laravel, with the actions defined in PHP.
Laravel SweetAlert2
SweetAlert2 popups, toasts and confirm dialogs from Laravel controllers, Livewire and Alpine.
Laralink
Work on a Composer package inside the Laravel app that uses it, then switch back to the Packagist release.
Refresher
A drop-in replacement for Laravel's RefreshDatabase that restores a cached snapshot of your test database instead of running migrate:fresh.
Kiln
Artisan commands and an optional signed HTTP route to clear, warm and inspect OPcache when you deploy.
Laravel Media Viewer
Show private PDFs, video and audio from any Laravel disk without making the files public.
Laravel Swiper
Swiper sliders, carousels and image galleries for Laravel as Blade components.
Laravel MySQL Advisor
Read-only health checks for the MySQL or MariaDB database behind your Laravel app, with the SQL and the Laravel migration to fix a problem wherever a fix applies.
Laravel Webhook Console
A dashboard and replay tool for the webhooks spatie/laravel-webhook-client stores in your Laravel app.