Skip to content

Your first table

After installation, three steps add a users page. Two optional changes then show more of what a table can do. The example uses Laravel’s React starter kit, its User model and an account you can sign in with; in another app, keep your own page names and layout.

Laravel

app/Http/Controllers/UserController.php
<?php
namespace App\Http\Controllers;
use App\Models\User;
use Inertia\Inertia;
use Inertia\Response;
use InertiaX\Table\Columns\NumberColumn;
use InertiaX\Table\Columns\TextColumn;
use InertiaX\Table\Table;
class UserController
{
public function index(): Response
{
return Inertia::render('users/index', [
Table::make('users')
->data(User::class)
->addColumns([
NumberColumn::make('id')->numberCast('int')->visible(false),
TextColumn::make('name')->searchable()->sortable(),
TextColumn::make('email'),
]),
]);
}
}

The id column identifies each row; visible(false) hides it.

Create the page for users/index. The starter kit’s layout wraps it, and the highlighted lines are the InertiaX part:

React

resources/js/pages/users/index.tsx
import { Head } from '@inertiajs/react';
import { AppTable } from '@/components/app-table';
export default function UsersIndex() {
return (
<>
<Head title="Users" />
<section className="m-4 rounded-xl border p-6">
<h1 className="text-lg font-semibold">Users</h1>
<p className="mb-4 text-sm text-muted-foreground">
Everyone who can sign in.
</p>
<AppTable prop="users" />
</section>
</>
);
}

Add it to the starter kit’s group for signed-in users:

Laravel

routes/web.php
use App\Http\Controllers\UserController;
Route::middleware(['auth', 'verified'])->group(function () {
// ...
Route::get('users', [UserController::class, 'index'])->name('users.index');
});

That’s the whole table. A new app may have only your own account, so add some users first:

Terminal window
php artisan tinker --execute="App\Models\User::factory(30)->create()"

Sign in and open /users: their names and email addresses appear in a styled table. Search for part of a name, click the Name heading to sort, and page through the rows.

Each change below edits only UserController; the React page stays as it is.

Make the email column filterable:

Laravel

->addColumns([
NumberColumn::make('id')->numberCast('int')->visible(false),
TextColumn::make('name')->searchable()->sortable(),
TextColumn::make('email')->filterable(),
]),

Automatic filters are on by default, so a filterable column gets a filter with comparisons for its type. Reload /users and click the new Filter button: choose Email, change Equals to Ends With, enter example.org and click Add filter. The rows narrow to those addresses, and the filter stays in the URL, so a reload or a shared link keeps it.

Or let InertiaX infer the columns and filters

Section titled “Or let InertiaX infer the columns and filters”

In UserController, replace the return statement in index() with this one, which infers the columns from the User model; the NumberColumn and TextColumn imports are then unused. Inferred columns aren’t searchable, sortable or filterable until the table turns that on:

Laravel

return Inertia::render('users/index', [
Table::make('users')
->data(User::class)
->autoColumns()
->searchableColumns()
->sortableColumns()
->filterableColumns(),
]);

Reload /users. If you searched or filtered earlier, the URL keeps it: clear the search box and click Clear filters to see every user. The table now shows every field the model serializes, such as Email Verified At and Created At, without $hidden fields such as the password. Text columns are searchable, and every column you see sorts and has a filter for its type.

A column that can be null also gets Is empty. The factory users are all verified, so add one that isn’t:

Terminal window
php artisan tinker --execute="App\Models\User::factory()->unverified()->create()"

Then filter Email Verified At by Is empty to find it.

Inference shows whatever the model serializes, so review those fields before you ship the page; see Choose fields deliberately. To leave a field out or customize one, see Infer columns from a model.

Choose what your page needs next:

In production, pass a query scoped to your application’s authorization and access rules. See Authorization & security.

View Markdown