PHPackages                             adesin-fr/inertiajs-tables-laravel-query-builder - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. adesin-fr/inertiajs-tables-laravel-query-builder

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

adesin-fr/inertiajs-tables-laravel-query-builder
================================================

Inertia.js Front-end Components for Spatie's Laravel Query Builder

3.6.2(3mo ago)261.7k↓45%1[1 issues](https://github.com/Adesin-fr/inertiajs-tables-laravel-query-builder/issues)MITPHPPHP ^8.2

Since Jun 14Pushed 3mo ago1 watchersCompare

[ Source](https://github.com/Adesin-fr/inertiajs-tables-laravel-query-builder)[ Packagist](https://packagist.org/packages/adesin-fr/inertiajs-tables-laravel-query-builder)[ Docs](https://github.com/Adesin-Fr/inertiajs-tables-laravel-query-builder)[ GitHub Sponsors](https://github.com/AdesinFr)[ RSS](/packages/adesin-fr-inertiajs-tables-laravel-query-builder/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (10)Dependencies (3)Versions (11)Used By (0)

Inertia.js Tables for Laravel Query Builder
===========================================

[](#inertiajs-tables-for-laravel-query-builder)

[![Latest Version on NPM](https://camo.githubusercontent.com/70d35e3bf47622554bbe17e8e3626e494c510a41223740e8255c6ad21b019011/68747470733a2f2f696d672e736869656c64732e696f2f6e706d2f762f4061646573696e2d66722f696e65727469616a732d7461626c65732d6c61726176656c2d71756572792d6275696c6465722e7376673f7374796c653d666c61742d737175617265)](https://npmjs.com/package/@adesin-fr/inertiajs-tables-laravel-query-builder)[![npm](https://camo.githubusercontent.com/421def0b605a106e58f89eb827351c072237f890c14fbc8c18eddfa441de9682/68747470733a2f2f696d672e736869656c64732e696f2f6e706d2f64742f4061646573696e2d66722f696e65727469616a732d7461626c65732d6c61726176656c2d71756572792d6275696c6465722e7376673f7374796c653d666c61742d737175617265)](https://www.npmjs.com/package/@adesin-fr/inertiajs-tables-laravel-query-builder)[![Latest Version on Packagist](https://camo.githubusercontent.com/df0c58dee94e63cf9f56eaa2d7e5049cc4ae9772856ef4f8e7426811fb8641f5/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f61646573696e2d66722f696e65727469616a732d7461626c65732d6c61726176656c2d71756572792d6275696c6465722e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/adesin-fr/inertiajs-tables-laravel-query-builder)[![Software License](https://camo.githubusercontent.com/55c0218c8f8009f06ad4ddae837ddd05301481fcf0dff8e0ed9dadda8780713e/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d627269676874677265656e2e7376673f7374796c653d666c61742d737175617265)](LICENSE.md)

This package provides a *DataTables-like* experience for [Inertia.js](https://inertiajs.com/) with support for searching, filtering, sorting, toggling columns, column reordering, column pinning, and pagination. It generates URLs that can be consumed by Spatie's excellent [Laravel Query Builder](https://github.com/spatie/laravel-query-builder) package, with no additional logic needed. The components are styled with [Tailwind CSS 3.0](https://tailwindcss.com/), but it's fully customizable with slots. The data refresh logic is based on Inertia's [Ping CRM demo](https://github.com/inertiajs/pingcrm).

This package is a fork of \[protonemedia/inertiajs-tables-laravel-query-builder\], Since it has been abandonned in favor of a commercial project.

[![Inertia.js Table for Laravel Query Builder](https://user-images.githubusercontent.com/8403149/177773377-86c32d69-8f86-47e4-8063-ea227e480d10.mp4)](https://user-images.githubusercontent.com/8403149/177773377-86c32d69-8f86-47e4-8063-ea227e480d10.mp4)

Features
--------

[](#features)

- **Fluent API**: New intuitive API for single and multiple tables ✅ **NEW!**
- **Infinite Scrolling**: Automatic infinite scrolling with seamless data loading ✅ **NEW!**
- **CSV Export**: Automatic CSV export with all filtered data ✅ **NEW!**
- **Number Filters**: Advanced number filtering with multiple comparison operators ✅ **NEW!**
- **Multiple Tables**: Support for multiple independent tables in a single view ✅ **NEW!**
- Auto-fill: auto generates `thead` and `tbody` with support for custom cells
- Global Search
- Search per field
- Select filters
- **Column Filters**: Add filter icons directly in column headers for intuitive filtering
- Toggle columns
- Sort columns
- **Column Reordering**: Drag and drop columns to reorder them with persistent state ✅ **NEW!**
- **Column Pinning**: Pin important columns to prevent them from being hidden ✅ **NEW!**
- Pagination (support for Eloquent/API Resource/Simple/Cursor)
- Automatically updates the query string (by using [Inertia's replace](https://inertiajs.com/manual-visits#browser-history) feature)
- Customizable header and body cells classes
- **Custom row styling**: Apply conditional CSS classes to table rows based on data
- Resizeable columns ✅

Compatibility
-------------

[](#compatibility)

- [Vue 3](https://v3.vuejs.org/guide/installation.html)
- [Laravel 11](https://laravel.com/)
- [Inertia.js](https://inertiajs.com/)
- [Tailwind CSS v3](https://tailwindcss.com/) + [Forms plugin](https://github.com/tailwindlabs/tailwindcss-forms)
- PHP 8.2+

Installation
------------

[](#installation)

You need to install both the server-side package and the client-side package. Note that this package is only compatible with Laravel 10, Vue 3.0, and requires the Tailwind Forms plugin.

### Server-side installation (Laravel)

[](#server-side-installation-laravel)

You can install the package via composer:

```
composer require adesin-fr/inertiajs-tables-laravel-query-builder
```

Fluent API ✨ **NEW!**
---------------------

[](#fluent-api--new)

The package now provides a modern fluent API that makes table configuration more intuitive and powerful.

### Single Table

[](#single-table)

For single table views, use the `InertiaTable::make()` method with a fluent syntax:

```
use AdesinFr\LaravelQueryBuilderInertiaJs\InertiaTable;
use Spatie\QueryBuilder\AllowedFilter;
use Spatie\QueryBuilder\QueryBuilder;

// Method 1: Direct QueryBuilder with callback
return InertiaTable::make()
    ->withQueryBuilder(
        QueryBuilder::for(User::class)
            ->allowedFilters([
                AllowedFilter::partial('name'),
                AllowedFilter::exact('status'),
                NumberFilter::getQueryBuilderFilter('age'),
            ])
            ->allowedSorts(['name', 'email', 'created_at'])
            ->defaultSort('name')
    )
    ->column('name', 'Name', sortable: true, searchable: true)
    ->column('email', 'Email', sortable: true, searchable: true)
    ->column('status', 'Status')
    ->column('age', 'Age', sortable: true)
    ->withGlobalSearch()
    ->selectFilter('status', [
        'active' => 'Active',
        'inactive' => 'Inactive',
    ])
    ->numberFilter('age', 'Age')
    ->render('Users/Index');

// Method 2: QueryBuilder callback (useful for multi-table setups)
return InertiaTable::make()
    ->withQueryBuilderCallback(function () {
        return QueryBuilder::for(User::class)
            ->allowedFilters([
                AllowedFilter::partial('name'),
                AllowedFilter::exact('status'),
                NumberFilter::getQueryBuilderFilter('age'),
            ])
            ->allowedSorts(['name', 'email', 'created_at'])
            ->defaultSort('name');
    })
    ->column('name', 'Name', sortable: true, searchable: true)
    ->column('email', 'Email', sortable: true, searchable: true)
    ->withResource(\App\Http\Resources\UserResource::class) // Optional resource transformation
    ->render('Users/Index');
```

### Multiple Tables

[](#multiple-tables)

For views with multiple tables, use the `InertiaTable::view()` method:

```
use AdesinFr\LaravelQueryBuilderInertiaJs\InertiaTable;

return InertiaTable::view('Dashboard/Index')
    ->table('users', function (InertiaTable $table) {
        $table->withQueryBuilderCallback(function () {
            // Configure query parameters for this table
            InertiaTable::updateQueryBuilderParameters('users');

            return QueryBuilder::for(User::class)
                ->allowedFilters([AllowedFilter::partial('name')])
                ->allowedSorts(['name', 'email'])
                ->defaultSort('name');
        })
        ->column('name', 'Name', sortable: true, searchable: true)
        ->column('email', 'Email', sortable: true)
        ->withGlobalSearch()
        ->paginateMethod('paginate');
    })
    ->table('products', function (InertiaTable $table) {
        $table->withQueryBuilderCallback(function () {
            // Configure query parameters for this table
            InertiaTable::updateQueryBuilderParameters('products');

            return QueryBuilder::for(Product::class)
                ->allowedFilters([
                    AllowedFilter::partial('title'),
                    NumberFilter::getQueryBuilderFilter('price')
                ])
                ->allowedSorts(['title', 'price'])
                ->defaultSort('title');
        })
        ->column('title', 'Title', sortable: true, searchable: true)
        ->column('price', 'Price', sortable: true)
        ->numberFilter('price', 'Price')
        ->paginateMethod('simplePaginate');
    })
    ->with(['customData' => 'Additional data for the view'])
    ->render();
```

### Additional Fluent API Methods

[](#additional-fluent-api-methods)

The fluent API provides many configuration options:

```
return InertiaTable::make()
    ->name('custom-table')              // Table name for multi-table setups
    ->pageName('customPage')            // Custom pagination parameter name
    ->perPageOptions([10, 25, 50])     // Available per-page options
    ->defaultSort('created_at')         // Default sorting column
    ->handleExport(true)                // Enable/disable CSV export (default: true)
    ->paginateMethod('simplePaginate')  // Pagination method
    ->withResource(\App\Http\Resources\UserResource::class) // Resource transformation
    ->with(['additional' => 'data'])    // Additional data for the view
    ->render('Users/Index');
```

Traditional API (Legacy)
------------------------

[](#traditional-api-legacy)

You can still use the traditional callbackF-based API if needed:

```
return Inertia::render('Users/Index')->table(function (InertiaTable $table) {
    $table->searchInput('name');
    $table->selectFilter('status', ['active' => 'Active']);
});
```

Configuration
-------------

[](#configuration)

The package will automatically register the Service Provider which provides a `table` method you can use on an Interia Response.

### Getting started with Traditional API

[](#getting-started-with-traditional-api)

By default, the package will search for the `routes/web.php` file and check for a route with the "search" name. If you have different setup, you can define the route in your configuration file.

As described above, the package will detect if you use [Spatie's Query Builder](https://github.com/spatie/laravel-query-builder). This usually means the root you're using looks somewhat like this:

```
$users = QueryBuilder::for(User::class)->paginate($request->perPage ?: 10);

return Inertia::render('Users/Index', [
    'users' => $users
]);
```

#### Search fields

[](#search-fields)

With the `searchInput` method, you can specify which attributes are searchable. Search queries are passed to the URL query as a `filter`. This integrates seamlessly with the [filtering feature](https://spatie.be/docs/laravel-query-builder/v5/features/filtering) of the Laravel Query Builder package.

Though it's enough to pass in the column key, you may specify a custom label and default value.

```
use AdesinFr\LaravelQueryBuilderInertiaJs\InertiaTable;

Inertia::render('Page/Index')->table(function (InertiaTable $table) {
	$table->searchInput('name');

	$table->searchInput(
		key: 'framework',
		label: 'Find your framework',
		defaultValue: 'Laravel'
	);
});
```

#### Select Filters

[](#select-filters)

Select Filters are similar to search fields but use a `select` element instead of an `input` element. This way, you can present the user a predefined set of options. Under the hood, this uses the same filtering feature of the Laravel Query Builder package.

The `selectFilter` method requires two arguments: the key, and a key-value array with the options.

```
Inertia::render('Page/Index')->table(function (InertiaTable $table) {
	$table->selectFilter('language_code', [
		'en' => 'Engels',
		'nl' => 'Nederlands',
	]);
});
```

The `selectFilter` will, by default, add a *no filter* option to the array. You may disable this or specify a custom label for it.

```
Inertia::render('Page/Index')->table(function (InertiaTable $table) {
	$table->selectFilter(
		key: 'language_code',
		options: $languages,
		label: 'Language',
		defaultValue: 'nl',
		noFilterOption: true,
		noFilterOptionLabel: 'All languages'
	);
});
```

#### Boolean Filters

[](#boolean-filters)

This way, you can present the user a toggle. Under the hood, this uses the same filtering feature of the Laravel Query Builder package.

The `toggleFilter` method requires one argument: the key.

```
Inertia::render('Page/Index')->table(function (InertiaTable $table) {
	$table->toggleFilter('is_verified');
});
```

You can specify a custom label for it and a default value.

```
Inertia::render('Page/Index')->table(function (InertiaTable $table) {
	$table->toggleFilter(
		key: 'is_verified',
		label: 'Is email verified',
		defaultValue: true,
	);
});
```

#### Number range Filters

[](#number-range-filters)

This way, you can present the user a toggle. Under the hood, this uses the same filtering feature of the Laravel Query Builder package.

The `numberRangeFilter` method requires two arguments: the key and the max value.

```
Inertia::render('Page/Index')->table(function (InertiaTable $table) {
	$table->numberRangeFilter('invoice_recall_count', 5);
});
```

You can specify a some other params.

```
Inertia::render('Page/Index')->table(function (InertiaTable $table) {
	$table->numberRangeFilter(
		key: 'invoice_recall_count',
		max: 5,
		min: 0,
		prefix: '',
		suffix: '',
		step: 1,
		label: 'Invoice recall count',
		defaultValue: [1,4],
	);
});
```

You need to use a custom allowed filter for this filter.

```
$users = QueryBuilder::for(/*...*/)
			->allowedFilters([NumberRangeFilter::getQueryBuilderFilter('invoice_recall_count')]);
```

#### Number Filters ✨ **NEW!**

[](#number-filters--new)

The `numberFilter` provides advanced comparison operations similar to date filters. You can filter by exact match, greater than, less than, between ranges, and more.

```
Inertia::render('Page/Index')->table(function (InertiaTable $table) {
	$table->numberFilter('age');
});
```

The Number Filter supports 6 different comparison operations:

- **Exact**: Find records with exact value
- **Greater than**: Find records greater than specified value
- **Greater than or equal**: Find records greater than or equal to specified value
- **Less than**: Find records less than specified value
- **Less than or equal**: Find records less than or equal to specified value
- **Between**: Find records within a specified range

You can customize the filter with additional parameters:

```
Inertia::render('Page/Index')->table(function (InertiaTable $table) {
	$table->numberFilter(
		key: 'age',
		label: 'Filter by age',
		defaultOperation: 'greater_than',
		defaultValue: 18,
		column_key: 'age' // Associate with a specific column
	);
});
```

You need to use the custom NumberFilter for this filter:

```
use AdesinFr\LaravelQueryBuilderInertiaJs\Filters\NumberFilter;

$users = QueryBuilder::for(User::class)
		->allowedFilters([
			NumberFilter::getQueryBuilderFilter('age')
		]);
```

```
$users = QueryBuilder::for(/*...*/)
			->allowedFilters([NumberRangeFilter::getQueryBuilderFilter('invoice_recall_count')]);
```

#### Column Filters ✨ **NEW!**

[](#column-filters--new)

You can now place filter icons directly in column headers for a more intuitive user experience. Each filter can be associated with a specific column using the `column_key` parameter.

```
Inertia::render('Page/Index')->table(function (InertiaTable $table) {
	$table->column('name', 'Name')
		  ->column('status', 'Status')
		  ->column('email', 'Email')
		  ->column('created_at', 'Created');

	// Associate a select filter with the 'status' column
	$table->selectFilter(
		key: 'status',
		options: [
			'active' => 'Active',
			'inactive' => 'Inactive',
			'pending' => 'Pending'
		],
		label: 'Status',
		column_key: 'status' // 🎯 Associates the filter with the status column
	);

	// Associate a toggle filter with the 'email' column
	$table->toggleFilter(
		key: 'email_verified',
		label: 'Email Verified',
		column_key: 'email'
	);

	// Associate a number range filter with the 'created_at' column
	$table->numberRangeFilter(
		key: 'days_since_creation',
		max: 365,
		min: 0,
		label: 'Days Since Creation',
		column_key: 'created_at'
	);
});
```

**Features:**

- 🎯 **Visual Association**: Filter icons appear directly in column headers
- 🎨 **Active State Indicator**: Icons change color when filters are applied
- 📱 **Responsive Dropdown**: Clean dropdown interface for filter options
- 🔄 **Backward Compatible**: Existing filters without `column_key` still work in the global filter bar

**Benefits:**

- More intuitive user experience
- Better visual organization
- Space-efficient interface
- Clear association between filters and data columns

For detailed examples and usage, see [COLUMN\_FILTERS.md](COLUMN_FILTERS.md).

#### Columns

[](#columns)

With the `column` method, you can specify which columns you want to be toggleable, sortable, and searchable. You must pass in at least a key or label for each column.

```
Inertia::render('Page/Index')->table(function (InertiaTable $table) {
	$table->column('name', 'User Name');

	$table->column(
		key: 'name',
		label: 'User Name',
		canBeHidden: true,
		hidden: false,
		sortable: true,
		searchable: true
		headerClass: 'hidden md:table-cell', // This cell will be hidden on small screens
		bodyClass: 'hidden md:table-cell', // This cell will be hidden on small screens
	);
});
```

The `searchable` option is a shortcut to the `searchInput` method. The example below will essentially call `$table->searchInput('name', 'User Name')`.

#### Global Search

[](#global-search)

You may enable Global Search with the `withGlobalSearch` method, and optionally specify a placeholder.

```
Inertia::render('Page/Index')->table(function (InertiaTable $table) {
	$table->withGlobalSearch();

	$table->withGlobalSearch('Search through the data...');
});
```

If you want to enable Global Search for every table by default, you may use the static `defaultGlobalSearch` method, for example, in the `AppServiceProvider` class:

```
InertiaTable::defaultGlobalSearch();
InertiaTable::defaultGlobalSearch('Default custom placeholder');
InertiaTable::defaultGlobalSearch(false); // disable
```

#### Example controller

[](#example-controller)

Here are examples using both the new fluent API and the traditional API:

#### Fluent API Example (Recommended)

[](#fluent-api-example-recommended)

```
