Custom sources
Implement TableSource::fetch() when an API or repository should handle the table’s search,
filters, sorting, and pagination. InertiaX passes the request as a TableQuery; your source
returns a TableResult containing the requested rows and the total number of matches.
Wrap a repository
Section titled “Wrap a repository”This adapter wraps a UserRepository that returns a Laravel LengthAwarePaginator.
Inside fetch(), forward the table request to the repository:
Laravel
return TableResult::fromPaginator($this->users->page( search: $query->search, sorting: $query->effectiveSorting(), page: $query->page, pageSize: $query->pageSize,), $query);Pass page and pageSize explicitly so Laravel does not read an unrelated page URL parameter.
effectiveSorting() adds the row key as a tie-breaker when an active sort omits it.
The complete example includes the class, repository, and checks
for operations the repository does not support.
Define and render the table
Section titled “Define and render the table”Pass the source class to data(). Laravel resolves its constructor dependencies. Configure only
the controls the repository supports: name search, ID/name sorting, and pagination. This repository
has no filter support, so disable automatic filters.
Laravel
use App\Sources\RepositoryUsersSource;use Inertia\Inertia;use InertiaX\Table\Columns\NumberColumn;use InertiaX\Table\Columns\TextColumn;use InertiaX\Table\Table;
return Inertia::render('users/index', [ Table::make('repository_users') ->data(RepositoryUsersSource::class) ->addColumns([ NumberColumn::make('id', 'ID')->sortable(), TextColumn::make('name', 'Name')->sortable()->searchable(), TextColumn::make('email', 'Email'), ]) ->autoFilters(false),]);React
import { AppTable } from '@/components/app-table';
export default function UsersIndex() { return <AppTable prop="repository_users" />;}Return the PHP example from a controller action with a route, as in Your first table.
The id column identifies each row; use rowKey() for another key.
Scope your repository’s query to records the current user may view. See
Authorization & security.
Apply search, filters, and sorting across the matching dataset before selecting a page; return the total before pagination. InertiaX formats the returned cells without querying the rows again.
Use an inline callback
Section titled “Use an inline callback”For a small adapter, use CallbackSource instead of creating a source class:
Laravel
use App\Sources\UserRepository;use InertiaX\Table\Sources\CallbackSource;use InertiaX\Table\Sources\TableQuery;use InertiaX\Table\Sources\TableResult;
$users = app(UserRepository::class);$source = CallbackSource::make( fn (TableQuery $query): TableResult => TableResult::fromPaginator( $users->page( search: $query->search, sorting: $query->effectiveSorting(), page: $query->page, pageSize: $query->pageSize, ), $query, ),);Use data($source) with the same columns and settings shown above. The callback runs after
InertiaX validates the request. A bare data(fn () => ...) runs earlier to choose a source;
it does not receive the completed query.
Configure a source instance
Section titled “Configure a source instance”Pass an instance when you already have a configured repository:
Laravel
$source = new RepositoryUsersSource($repository);Use data($source) inline or on a reusable Table class.
A fluent data() call overrides the class’s data() declaration for that use.
Add filters or connect an API
Section titled “Add filters or connect an API”Declare filters and clauses that your source can translate. The API source example shows a reusable Table with select options, nested filters, and field mappings for an HTTP service. It includes a local fake service so you can run the example without API credentials.
See Source API for query fields, options callbacks, and result requirements. For data that InertiaX should query automatically, use Eloquent or collections.
Complete repository example
Section titled “Complete repository example”Use these classes with the table configuration above. The source checks its provider’s limits; InertiaX validates the table request but cannot determine which operations your repository supports.
RepositoryUsersSource.php — complete source
<?php
namespace App\Sources;
use InertiaX\Table\Sources\Contracts\TableSource;use InertiaX\Table\Sources\TableQuery;use InertiaX\Table\Sources\TableResult;use InvalidArgumentException;
final class RepositoryUsersSource implements TableSource{ public function __construct(private UserRepository $users) {}
public function fetch(TableQuery $query): TableResult { if (! $query->paginationEnabled || $query->filters['conditions'] !== []) { throw new InvalidArgumentException('User repository requires pagination and does not support filters.'); } if (array_diff($query->searchableColumns, ['name']) !== []) { throw new InvalidArgumentException('User repository searches only names.'); } foreach ($query->effectiveSorting() as $sort) { if (! in_array($sort['key'], ['id', 'name'], true)) { throw new InvalidArgumentException("User repository cannot sort [{$sort['key']}]."); } }
return TableResult::fromPaginator($this->users->page( search: $query->search, sorting: $query->effectiveSorting(), page: $query->page, pageSize: $query->pageSize, ), $query); }}UserRepository.php — example repository
<?php
namespace App\Sources;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;use Illuminate\Support\Facades\DB;
/** A pre-existing repository can keep its own query and pagination implementation. */final class UserRepository{ public function page(string $search, array $sorting, int $page, int $pageSize): LengthAwarePaginator { $query = DB::table('users')->select(['id', 'name', 'email']); if ($search !== '') { $query->where('name', 'like', '%'.$search.'%'); } foreach ($sorting as $sort) { $query->orderBy($sort['key'], $sort['direction']); }
return $query->paginate($pageSize, page: $page); }}