Skip to content

Installation

Lattice adds a server-driven UI layer to a Laravel application running Inertia with the React adapter. Installing it has two halves: the Composer package on the backend and the React renderer on the frontend.

RequirementVersion
PHP8.4+
Laravel11, 12, or 13
Inertia (Laravel + React)v3
React19
Tailwind CSS4

Lattice ships as two coordinated packages:

  • lattice-php/lattice on Composer — the PHP layer that describes pages, forms, tables, actions, and menus and serializes them to typed component trees.
  • @lattice-php/lattice on npm — the React renderer that turns those trees into rendered UI in the browser.

Both packages are cut from the same release, so a given Composer version always has a matching npm version — the renderer can’t drift from the server that serialized the page.

Terminal window
composer require lattice-php/lattice

The service provider is registered automatically through Laravel package discovery, so there is nothing to add to bootstrap/providers.php.

Publish config/lattice.php to customize discovery paths, endpoints, and middleware:

Terminal window
php artisan vendor:publish --tag="lattice-config"

See Configuration for what each option controls.

The browser needs the Lattice React renderer to turn the server’s component trees into rendered UI. Install it, import its stylesheet, and register the page component.

Terminal window
npm install @lattice-php/lattice

The renderer’s UI libraries (Radix, TipTap, Lucide, …) come with it. react, react-dom, and @inertiajs/react are peer dependencies you already have in a Laravel React app. Styling is driven by Tailwind CSS v4:

Terminal window
npm install -D tailwindcss @tailwindcss/vite tw-animate-css

Import Lattice’s stylesheet from your main CSS entry, after Tailwind. It defines the theme tokens the components use and registers the package’s compiled output with Tailwind automatically, so no extra @source line is needed:

resources/css/app.css
@import "tailwindcss";
@import "tw-animate-css";
@import "@lattice-php/lattice/css";

The component tokens (--lt-*) fall back to sensible defaults, so the UI is styled out of the box. They also read from shadcn-style variables (--background, --primary, …) when you define them, which lets Lattice inherit an existing theme.

Every Lattice route resolves to the same Inertia page component, lattice/page, which the package provides. In your Inertia entrypoint, resolve that name to Lattice’s page component and fall back to your own pages for anything else:

resources/js/app.tsx
import "../css/app.css";
import { createInertiaApp } from "@inertiajs/react";
import { createRoot } from "react-dom/client";
import LatticePage from "@lattice-php/lattice/page";
createInertiaApp({
resolve: (name) => {
if (name === "lattice/page") {
return { default: LatticePage };
}
const pages = import.meta.glob("./Pages/**/*.tsx", { eager: true });
return pages[`./Pages/${name}.tsx`];
},
setup({ el, App, props }) {
if (el) {
createRoot(el).render(<App {...props} />);
}
},
});

That single resolve branch is all an application needs for every page Lattice routes.

The renderer resolves component types from a registry. Lattice exports the pieces you need to extend or replace it:

import { Provider, Renderer, registry, createRegistry, extendRegistry } from "@lattice-php/lattice";

Wrap your tree in Provider with a custom registry to register your own component types, or use extendRegistry to add to the defaults. Heavy built-ins (forms, tables, the rich editor) are registered lazily and code-split, so you only ship what a page actually renders.