Skip to content

Tables

A table is a listing backed by a data source. You declare columns in PHP; Lattice renders the React table and handles sorting, filtering, and pagination, fetching rows from the table’s endpoint. Nothing about a table is tied to a database — a table is a TableDefinition plus a source that turns a request into rows. An Eloquent source ships out of the box, and an in-memory array, an HTTP API, or a search index works just as well.

#[AsTable('app.products')]
class ProductsTable extends TableDefinition
{
public function columns(): array
{
return [
TextColumn::make('name')->sortable()->filterable(),
NumberColumn::make('price')->sortable()->filterable(),
BooleanColumn::make('featured'),
TextColumn::make('updated_at')->label('Updated')->dateTime()->sortable(),
];
}
public function source(): TableSource
{
return new CallbackTableSource(
fn (TableQuery $query) => TableResult::fromItems($this->products()),
);
}
}

Every sort, filter, and page change is a fresh GET to the table’s endpoint. The request becomes a TableQuery that the data source turns into rows — so the React table never holds the full dataset, it asks for the page it needs:

Fetching a page of rows through the data source

Extend TableDefinition and implement its two abstract methods — columns() and source(). The #[AsTable] attribute gives the table a stable id so it can be discovered and addressed by its own endpoint.

use Lattice\Lattice\Attributes\AsTable;
use Lattice\Lattice\Tables\CallbackTableSource;
use Lattice\Lattice\Tables\Columns\NumberColumn;
use Lattice\Lattice\Tables\Columns\TextColumn;
use Lattice\Lattice\Tables\Contracts\TableSource;
use Lattice\Lattice\Tables\TableDefinition;
use Lattice\Lattice\Tables\TableQuery;
use Lattice\Lattice\Tables\TableResult;
#[AsTable('app.products')]
class ProductsTable extends TableDefinition
{
public function columns(): array
{
return [
TextColumn::make('name')->sortable()->filterable(),
NumberColumn::make('price')->sortable(),
];
}
public function source(): TableSource
{
return new CallbackTableSource(
fn (TableQuery $query) => TableResult::fromItems($this->rows()),
);
}
}

Everything except the source is declared the same way whatever backs the table. TableDefinition exposes these hooks beyond columns() and source():

Method Purpose
columns() The columns to render — see Columns.
source() The data source that turns a query into rows.
filters() Table-level dedicated filters.
perPage() Rows per page (default 25).
pagination() The pagination mode.
striped() Alternate row shading.
resizableColumns() Let users drag column borders to resize. Widths persist per table in the browser.
resizeIndicator() Show a drag indicator on column borders when resizing is enabled.
emptyLabel() Text shown when the table has no rows (default “No results”).
actionsLabel() Accessible label for the row-actions column (default “Actions”).
actions($row) Per-row actions.
bulkActions() Bulk actions for selected rows.

Each hook is a method you override. For example, to enable resizable columns with a drag indicator:

public function resizableColumns(): bool
{
return true;
}
public function resizeIndicator(): bool
{
return true;
}

Resized widths are remembered per table in the browser, with a control to reset them to the defaults.

Render a table on a page with Table::use(), passing the definition class:

use Lattice\Lattice\Tables\Components\Table;
Table::use(ProductsTable::class);

Table::lazy() renders the same table but defers the first data load until the component mounts — useful inside a tab or a section that isn’t visible on page load.

  • Data sources — the TableSource contract and backing a table with anything.
  • Eloquent tables — the database-backed source, builder(), and relation columns.
  • Columns — the column types and their display options.
  • Filtering — column filters and dedicated table filters.
  • Sorting & pagination — sortable columns and the pagination modes.
  • Actions — row and bulk actions.