Skip to main content

Inertia Table API Reference

This document provides a condensed API specification for implementing Inertia Tables in our Laravel/Inertia.js application.
Official Documentation: Inertia Table Introduction
Custom Extensions: Core Table Extensions - Custom traits, columns, and domain object integration

Quick Start

1. Generate Table Class

Official Documentation: Generating Tables
php artisan make:inertia-table UserTable

2. Backend Table Implementation

<?php

namespace App\Tables;

use App\Models\User;
use Illuminate\Database\Eloquent\Builder;
use InertiaUI\Table\Table;
use InertiaUI\Table\Columns\TextColumn;
use InertiaUI\Table\Columns\DateColumn;
use InertiaUI\Table\Columns\ActionColumn;
use InertiaUI\Table\Filters\TextFilter;
use InertiaUI\Table\Action;
use InertiaUI\Table\Export;
use Illuminate\Support\Collection;

class UserTable extends Table
{
    protected ?string $defaultSort = 'name';

    public function resource(): Builder|string
    {
        return User::query()->with(['permissions', 'roles', 'avatar']);
    }

    // Alternative: Use property for simple cases without eager loading
    // protected ?string $resource = User::class;

    public function columns(): array
    {
        return [
            TextColumn::make('name', __('Name'), sortable: true),
            TextColumn::make('email', __('Email'), sortable: true),
            DateColumn::make('created_at', __('Created At')),
            ActionColumn::new(__('Actions')),
        ];
    }

    public function filters(): array
    {
        return [
            TextFilter::make('name', __('Name')),
            TextFilter::make('email', __('Email')),
        ];
    }

    public function actions(): array
    {
        return [
            Action::make(__('Deactivate'), handle: fn (User $user) => $user->deactivate())
                ->asDangerButton()
                ->asBulkAction(),
        ];
    }

    public function exports(): array
    {
        return [
            Export::make(),
        ];
    }
}

3. Controller Usage

use App\Tables\UserTable;

class UserController
{
    public function index()
    {
        return inertia('Users', [
            'userTable' => UserTable::make(),
        ]);
    }
}

4. Frontend Implementation

Official Documentation: Basic Usage
Important: We use a custom TypeScript component, not the standard Inertia Table component.
import {
  InertiaTable,
  InertiaTableResource,
} from '@/core/components/inertia-table';

interface Props {
  userTable: InertiaTableResource;
}

export default function Users({ userTable }: Props) {
  return (
    <div>
      <InertiaTable
        resource={userTable}
        cell={{
          // Override specific column rendering
          name: ({ item, value }) => <MyCustomComponent data={item}>,
        }}
      />
    </div>
  );
}

Column Types & Options

Official Documentation: Columns
All columns support these common options:
OptionTypeDefaultDescription
sortablebooleanfalseEnable sorting
searchablebooleanfalseInclude in search
toggleablebooleantrueAllow hiding/showing
visiblebooleantrueInitial visibility
alignmentColumnAlignmentLeftText alignment
wrapbooleanfalseAllow text wrapping
truncateint|nullnullTruncate after N lines

TextColumn

// As chained method
TextColumn::make('name', __('Full Name'), sortable: true, searchable: true)
    ->mapAs(fn(string $name) => strtoupper($name));

// Or as constructor parameter with named arguments
TextColumn::make(
    'last_active_at',
    __('Last active'),
    sortable: true,
    mapAs: fn($value, User $user) => $user->last_active_at?->diffForHumans() ?? '-'
);

NumericColumn

NumericColumn::make('price', __('Price'))
    ->alignment(ColumnAlignment::Right);

DateColumn

DateColumn::make('created_at', __('Created'))
    ->format('d/m/Y')
    ->translate(); // Use Carbon translation

DateTimeColumn

DateTimeColumn::make('updated_at', __('Last Updated'))
    ->format('d/m/Y H:i:s')
    ->translate();

BooleanColumn

BooleanColumn::make('is_active', __('Active'))
    ->trueLabel(__('Yes'))
    ->falseLabel(__('No'))
    ->trueIcon('CheckCircleIcon')
    ->falseIcon('XCircleIcon');

ImageColumn

Official Documentation: Images
ImageColumn::make('avatar_url', __('Avatar'))
    ->rounded()
    ->size('large')
    ->squared();

BadgeColumn

BadgeColumn::make('status', __('Status'));

ActionColumn

ActionColumn::new(__('Actions')); // Only one per table

InlineEditColumn

Enables inline editing with CoreAction integration:
use Inly\Core\Tables\Columns\InlineEditColumn;

InlineEditColumn::make('title', __('Title'))
    ->coreAction(UpdateAddonAction::class, fn($addon) => ['id' => $addon->id])
    ->searchable()
    ->sortable();

// With different input types
->inputType('number')
->inputType('textarea')
->inputType('select')->options(['active' => 'Active', 'inactive' => 'Inactive'])
See: Core Action - Inline Edit Integration

Advanced Features

Icon Resolver

Inertia Table uses an icon resolver system to render icons throughout the table (actions, boolean columns, badges, etc.). We always use Lucide icons.

Global Icon Resolver

Set up a global icon resolver once in your application:
import { setIconResolver } from '@/core/components/inertia-table';
import { CheckCircle, XCircle, Edit, Trash } from 'lucide-react';

// Global icon resolver using Lucide React
setIconResolver((iconName: string) => {
  const icons = {
    CheckCircle,
    XCircle,
    Edit,
    Trash,
    // Add more icons as needed
  };

  return icons[iconName] || null;
});

Per-Table Icon Resolver

Override the global resolver for specific tables:
<InertiaTable
  resource={userTable}
  iconResolver={iconName => {
    // Custom icon resolution for this table
    return customIcons[iconName] || null;
  }}
/>

Icon Usage in Backend

Icons are referenced by string names in your table classes:
// Boolean column with icons
BooleanColumn::make('is_active', 'Active')
    ->trueIcon('CheckCircle')
    ->falseIcon('XCircle');

// Action with icon
Action::make('Edit', handle: fn (User $user) => redirect()->route('users.edit', $user))
    ->icon('Edit');

DTO Transformation

Official Documentation: Transform Model Data
Transform models to DTOs for frontend consumption:
use App\Data\ProductData;

class ProductTable extends Table
{
    public function transformModel(Model $model, array $data): array
    {
        return [
            ...$data,
            'data' => ProductData::from($model)->toArray(),
        ];
    }
}

Cell Overrides

Official Documentation: Slots
Override column rendering in the frontend:
<InertiaTable
  resource={productTable}
  cell={{
    mapping: ({ item, value, column }) => (
      <ProductMapping
        product={item}
        productStyles={productStyles}
        genderOptions={genderOptions}
      />
    ),
    status: ({ value }) => (
      <Badge variant={value === 'active' ? 'success' : 'secondary'}>
        {value}
      </Badge>
    ),
  }}
/>
Official Documentation: Row Links
Make entire rows clickable to navigate to detail pages:
// Or using named routes
TextColumn::make('email', __('Email'))
    ->route('users.edit', $user),

Sticky Columns

Official Documentation: Sticky Columns and Header
Make columns stick to the left side when scrolling horizontally:
public function columns(): array
{
    return [
        TextColumn::make('name', __('Name'))->stickable(), // Sticks to left
        TextColumn::make('email', __('Email')),
        TextColumn::make('phone', __('Phone')),
        // ... many more columns
        ActionColumn::new(__('Actions')), // Actions typically on the right
    ];
}

Empty State

Official Documentation: Empty State
Customize the message and actions when no data is found:
class UserTable extends Table
{
    protected array $emptyState = [
        'title' => __('No users found'),
        'description' => __('Get started by creating your first user.'),
        'icon' => 'Users',
        'actions' => [
            [
                'label' => __('Create User'),
                'url' => route('users.create'),
                'style' => 'primary',
                'icon' => 'Plus',
            ],
        ],
    ];
}

Common Filters

Official Documentation: Filtering

TextFilter

TextFilter::make('name', __('Name')),

SetFilter

SetFilter::make('status')->options([
    'active' => __('Active'),
    'inactive' => __('Inactive'),
]),

// Or from relationship
SetFilter::make('category')->pluckOptionsFromRelation('name'),

BooleanFilter

BooleanFilter::make('is_active', __('Active')),

DateFilter

DateFilter::make('created_at', __('Created Date'))->nullable(),

// Date range filter
DateFilter::make('created_at', __('Created Date'))
    ->range()
    ->nullable(),

RangeFilter

For numeric ranges like prices or quantities:
RangeFilter::make('price', __('Price Range'))
    ->min(0)
    ->max(1000)
    ->step(10),

Actions

Row Actions

Official Documentation: Row Actions
Action::make(__('Edit'), handle: fn (User $user) => redirect()->route('users.edit', $user))
    ->asLink()
    ->icon('Edit'),

Action::make(__('Delete'), handle: fn (User $user) => $user->delete())
    ->asDangerButton()
    ->icon('Trash')
    ->confirm(
        title: __('Delete User'),
        message: __('Are you sure?'),
        confirmButton: __('Delete'),
    ),

Bulk Actions

Official Documentation: Bulk Actions
Action::make(__('Activate Selected'))
    ->asBulkAction()
    ->icon('CheckCircle')
    ->handle(function (Collection $users) {
        $users->each->activate();
    }),

Exports

Official Documentation: Exporting
Configure table data exports to CSV/Excel.
use InertiaUI\Table\Table;

class UserTable extends Table
{
    public function exports(): array
    {
        return [
            Export::make(),
        ];
    }
}

Pagination

Official Documentation: Pagination
Inertia Table supports Laravel’s pagination out of the box and handles rendering. We’ve set a global default to 30, but you can override the count and also choose to provide multiple options if relevant.

Basic Pagination

// Specific count with no options
protected int $perPageOptions = [30];

// Multiple options
protected array $perPageOptions = [10, 25, 50, 100];

Multiple Tables

Official Documentation: Multiple Tables
You can have multiple independent tables on the same page. It’s critical to name tables when you have more than one on a page to prevent query string conflicts.

Why Name Tables?

The state of each table (filtering, sorting, pagination) is passed to the Laravel backend via the query string. Without naming, multiple tables would share the same query parameters, causing conflicts. For example:
❌ Without naming: ?sort=name&page=2
   (Both tables would try to use the same sort/page parameters)

✅ With naming: ?transaction[sort]=date&transaction[page]=1&account-transaction-request[sort]=created_at&account-transaction-request[page]=2
   (Each table maintains its own independent state)

Automatic Table Naming

CoreTable automatically names tables based on the class name, so you don’t need to manually call as():
  • TransactionTable → automatically named transaction
  • AccountTransactionRequestTable → automatically named account-transaction-request
  • UserTable → automatically named user
The naming convention:
  1. Removes the Table suffix from the class name
  2. Converts to kebab-case

Backend Setup

No manual naming needed - tables are automatically named:
class AccountController
{
    public function show(Account $account)
    {
        return inertia('AccountShow', [
            // Automatically named 'transaction'
            'transactionTable' => new TransactionTable($account),

            // Automatically named 'account-transaction-request'
            'transactionRequestTable' => new AccountTransactionRequestTable($account),
        ]);
    }
}

Manual Override

If you need to override the automatic name, you can still use as():
class DashboardController
{
    public function index()
    {
        return inertia('Dashboard', [
            'userTable' => UserTable::make()->as('custom-users'),
            'orderTable' => OrderTable::make()->as('custom-orders'),
        ]);
    }
}

Frontend Implementation

No changes needed on the frontend - the Table component automatically uses the table name to generate the query string:
interface Props {
  transactionTable: InertiaTableResource;
  transactionRequestTable: InertiaTableResource;
}

export default function AccountShow({
  transactionTable,
  transactionRequestTable,
}: Props) {
  return (
    <div className="space-y-8">
      <div>
        <h2>{__('Transaction History')}</h2>
        <InertiaTable resource={transactionTable} />
      </div>

      <div>
        <h2>{__('Transaction Requests')}</h2>
        <InertiaTable resource={transactionRequestTable} />
      </div>
    </div>
  );
}

Key Points

  • CoreTable automatically names tables based on class name (e.g., TransactionTabletransaction)
  • No manual naming needed - tables are automatically named to prevent conflicts
  • You can still override with as() if you need a custom name
  • Each table maintains its own independent state (pagination, sorting, filtering)
  • The frontend automatically handles the query string namespacing - no changes needed

Key Differences from Standard Inertia Table

  1. Custom Frontend Component: Import InertiaTable from @/core/components/inertia-table
  2. Table Naming: Use Table suffix (e.g., UserTable, ProductTable)
  3. Extended Base Class: Use CoreTable instead of Table for stats and row styling
  4. Custom Columns: DetailedLinkColumn, ObjectColumn, InlineEditColumn available
  5. Scout Integration: Optional SearchUsingScout trait available
  6. DTO Support: Use transformModel() method to convert models to DTOs
  7. TypeScript Props: InertiaTableResource type for table resources
See Core Table Extensions for detailed documentation on custom features.

Frontend Cell Override Props

When using cell overrides, the render function receives:
{
  item: any,           // The row data (item.data is the DTO if transformed)
  column: string,      // Column attribute name
  value: any,          // The column value
  image: any,          // Image data if applicable
  table: TableInstance, // Table instance methods
  actions: Actions,    // Action methods
}
This structure makes it easy to build dynamic, interactive tables with custom components while leveraging the power of Inertia Table’s backend features.