Skip to content

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.

Laravel 12 and 13 PHP 8.3+ v1.0.0 Free · MIT
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
A Grid.js customers table with a keyword search box, sortable Name, Email, Plan, MRR and Signed up columns, a Team column from a relation, dollar amounts and dates formatted, an Edit link on each row, and pagination reading Showing 1 to 8 of 108 results.
A Grid.js customers table with a keyword search box, sortable Name, Email, Plan, MRR and Signed up columns, a Team column from a relation, dollar amounts and dates formatted, an Edit link on each row, and pagination reading Showing 1 to 8 of 108 results.

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.

Need something built?

Free quote in 60 seconds