Laravel datatable with Grid.js

Laravel and Grid.js Blogpost Image

Published:March 23, 2021

Updated: November 15, 2023

Views: 9,367



A typo was fixed when generating the Single Action Controller. Also as the article mentions in the beginning we already assume you have a Laravel project, so in order to keep the tutorial short we didn't go through the required steps to scaffold a new project and seed the data. As for the final design ( on the screenshot ) it's based on Tabler, which is a free dashboard template for Bootstrap 5. Although not covered in this article (as the focus is not styling) Grid.js itself is highly customizable as explained in their docs.

In the world of web development, displaying large datasets to the user can sometimes be a challenging task. With the shift of Bootstrap away from jQuery, the popular DataTables plugin is no longer an option for displaying large chunks of data. This necessitates the search for alternatives. At Lucky Media, we always provide custom design solutions using TailwindCSS, so we sought a vanilla JavaScript implementation that could be customized to meet our needs.

Discovering Grid.js

During our search, we discovered Grid.js, a free and open-source JavaScript table plugin. It's compatible with most JavaScript frameworks, including React, Angular, Vue and VanillaJs. In this tutorial, we will guide you through setting up Grid.js with server-side rendering in a Laravel 8 project.

Note: This tutorial assumes that you already have a Laravel 8 project set up. If you need help with this, check out our Laravel development services.

Setting Up Grid.js

npm install gridjs

To begin, we'll install Grid.js in our project. We have a Clients model with more than 100 records, and we'll use Grid.js to display all this data on the frontend. You can also use a seeder for this purpose.

Creating a Single Action Controller

First, we'll create a single action controller that fetches all the clients and returns a JSON response that we can use on the frontend. To create the invokable controller, use the following command:

php artisan make:controller Actions\\FetchClientsController --invokable

This command stores our new invokable controller in an Actions folder, helping to keep our controllers clean and organized.

Here's what our FetchClientController looks like:


namespace App\Http\Controllers\Actions;

use App\Http\Controllers\Controller;
use \Illuminate\Http\JsonResponse;
use App\Models\Client;

class FetchClientsController extends Controller
    public function __invoke(): JsonResponse
        $clients = Client::all()
                return [
                    'id' => $client->id,
                    'full_name' => "$client->name $client->surname",
                    'number' => $client->number,
                    'street' => $client->street,
                    'edit_url' => route('clients.edit', $client->id)

        return response()->json($clients);

Here on the controller we get all our Clients, and then using the transform function we only return the needed fields in the frontend. Note the last line for edit_url, we will use it to automatically get the url for editing data so we can use it in the table.

Configuring Routes

We navigate to the routes folder and we access web.php file to reference our new controller like so:


use Illuminate\Support\Facades\Route;
use \App\Http\Controllers\ClientController;
use \App\Http\Controllers\Actions\FetchClientsController;

| Web Routes
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!


Route::group(['middleware' => 'auth'], function () {

    // Single Action Controllers
    Route::get('/clients/fetch', FetchClientsController::class)->name('clients.fetch');
    // Resource Controllers
    Route::resource('clients', ClientController::class);

Rendering the Table from JavaScript

Now, all we have to do is configure our index file in order to render our table from JavaScript.

In our resource/views/clients/index.blade.php we have the following code:

@extends('', ['title' => 'Clients'])

    <div class="row">
        <div class="col-lg-12">
            <div js-hook-url="{{ route('clients.fetch') }}" js-hook-table-client></div>

In this example we use two HTML attributes that we will use in our JavaScript file, the first one is for retrieving our URL where we fetch all the clients, and we are going to use the second one as a reference to render our table from JavaScript.

In our app.js file under resources/js/app.js we need to add the following code.

import { Grid, html } from "gridjs";
import "gridjs/dist/theme/mermaid.css";

const TABLE_CLIENTS = '[js-hook-table-client]'

// Get the table element
const table_clients_wrapper = document.querySelector(TABLE_CLIENTS);

// Get the url attribute
const table_clients_url = table_clients_wrapper.getAttribute('js-hook-url');

if (table_clients_wrapper) {
    const table_clients = new Grid({
        columns: [
                name: 'Full Name'
                name: 'Number'
                name: 'Street'
                name: 'Actions',
                // Here we inject our route edit
                formatter: (_, row) => html(`<a href='${row.cells[3].data}'>Edit</a>`)
        search: {
            enabled: true
        server: {
            // Here we give the URL we passed in the hook
            url: table_clients_url,
            then: data => => [table.full_name, table.number, table.street, table.edit_url]),
            handle: (res) => {
                // no matching records found
                if (res.status === 404) return {data: []};
                if (res.ok) return res.json();

                throw Error('oh no :(');
        pagination: {
            enabled: true,
            limit: 10,
            summary: false

So, what are we doing here? We instantiate a new Grid Class and pass down the options as objects. The columns array represents all the table columns, and the last item is used to inject our edit route. In the server object, we give the URL parameter that we called earlier, and after the data has been loaded, we just map each data that's coming from the server with their respective columns.

The pagination part is self-explanatory. We have enabled pagination and set the limit of rows to be displayed at 10.

You can read more about this in the official Grid.JS documentation which covers a lot of use cases.

After this, you have to run npm run dev in order to compile all the assets. If you did everything correctly then you should see the following screen:

Table Image

Supercharge Your Loading Times

Tip: You can use the following library to cache your models and supercharge your loading times. We highly recommended for large data: laravel-model-caching.

For more insights and tutorials, visit our blog. If you need professional help with your Laravel projects, don't hesitate to check out our Laravel development services.

Bring Your Ideas to Life 🚀

Accelerate Your Business with a Top Laravel Development Agency

If you need help with a Laravel project let's get in touch.

Lucky Media is proud to be recognized as a Top Laravel Development Agency by Clutch, a leading B2B ratings and reviews platform.

Our Services and Specializations

At Lucky Media, we offer a range of services including website development, web application development, and mobile apps development. We specialize in Statamic, React Native, Next.js, AI and ML solutions. We also provide staff augmentation and TALL stack development services.

Case Studies: Our Work in Action

For more insights into our work, check out our case studies on revolutionising lead generation with AI, customized coaching site, healthcare digitization, next-level performance, lead generation and patient journey, WordPress to Statamic migration, and improving user experience. These case studies provide a glimpse into how we tailor our technology choices to meet specific client needs and deliver exceptional results.

Stay up to date

Be updated with all news, products and tips we share!