What Is Lattice?
Lattice is a server-driven UI framework for Laravel that lets you describe your entire frontend — pages, forms, tables, and actions — using fluent PHP classes. It serializes that component tree into a typed payload, ships it over Inertia.js as a normal page visit, and a single React component resolves each node against a registry to render it.
The server becomes the single source of truth for what a screen is. The client's only job is rendering it.
Pages as PHP Classes
Every page extends a base Page class and carries an #[AsPage] attribute that registers its route automatically — no manual route entries needed. The UI is assembled in a render() method using fluent components like Stack, Grid, Heading, and Card:
#[AsPage(route: '/dashboard', middleware: ['web'])]
final class DashboardPage extends BasePage
{
public function render(PageSchema $schema): PageSchema
{
return $schema->schema([
Stack::make('dashboard')
->gap(Gap::Large)
->schema([
Heading::make('Dashboard'),
Grid::make('stats')
->columns(2)
->schema([
Card::make('Orders', '128 this week.'),
Card::make('Revenue', '$4,210 this week.'),
]),
]),
]);
}
}
Route-model binding works directly in the render() signature, and an authorize() method gates access before the page renders.
Forms Backed by Laravel Validation
Forms extend FormDefinition. Fields are declared in PHP with standard Laravel validation rules, and the handle() method runs on a successful submission. Dropping a form onto a page is a single fluent call:
Form::use(ProfileForm::class)
->method(HttpMethod::Patch)
->submitLabel('Save changes')
->precognitive(500)
->fill(['name' => $user->name, 'email' => $user->email]);
Adding ->precognitive(500) opts into live validation through Laravel Precognition with a 500 ms debounce.
Eloquent-Backed Tables
Tables extend EloquentTableDefinition. You declare columns and return a query builder; sorting, filtering, and pagination are handled automatically based on which columns you mark as sortable() or filterable():
#[AsTable('app.products')]
class ProductsTable extends EloquentTableDefinition
{
public function columns(): array
{
return [
TextColumn::make('name')->sortable()->filterable(),
NumberColumn::make('price')->sortable()->filterable(),
BooleanColumn::make('featured'),
];
}
public function builder(TableQuery $query): Builder
{
return Product::query();
}
}
Rendering it on a page uses the same ::use() pattern as forms: Table::use(ProductsTable::class).
Actions and Client Effects
Actions extend ActionDefinition and return an ActionResult carrying effects — instructions the client dispatches, such as a toast notification or a component reload:
public function handle(Request $request): ActionResult
{
$product = $this->product($request);
$product->update(['status' => 'archived']);
return ActionResult::success()
->toast(Variant::Success, 'Product archived.')
->reloadComponent('app.products');
}
Attached to a table row, the action carries the row's context to the server so handle() knows which record it's acting on.
Key Takeaways
- Zero manual routes —
#[AsPage]registers routes automatically by scanning configured paths. - PHP-only UI authoring — no JSX, no TypeScript component files for standard screens.
- Laravel-native validation — rules, Precognition live validation, and route-model binding all work as expected.
- Eloquent tables out of the box — sorting, filtering, and pagination are declared, not hand-coded.
- Server-side effects — actions return typed instructions (toast, redirect, reload) rather than views.
Lattice is open source. Full documentation, component reference, and theming options are available at latticephp.com, and the source is on GitHub.