Skip to content

Pages & Layouts

The frontend is React 19 with Inertia 3. Controllers return Inertia::render('users/index', [...]), and Inertia renders resources/js/pages/users/index.tsx with those props. There’s no client-side router and no API layer to maintain.

  • One folder per feature under resources/js/pages/: users/, roles/, activity/, settings/, auth/.
  • List pages keep their table columns in a sibling columns.tsx.
  • Each page is a default-exported component whose props match what the controller sends. Declare them with an interface:
interface IndexProps {
activities: Paginator<ActivityEntry>;
eventOptions: ActivityEventOption[];
}
export default function Index({ activities, eventOptions }: IndexProps) {
// …
}

Layouts are chosen centrally in resources/js/app.tsx by page name, so pages don’t wrap themselves:

layout: (name) => {
switch (true) {
case name === 'welcome':
return null;
case name.startsWith('auth/'):
return AuthLayout;
case name.startsWith('settings/'):
return [AppLayout, SettingsLayout];
default:
return AppLayout;
}
},
Pages Layout
welcome None
auth/* AuthLayout (a centered card)
settings/* AppLayout with SettingsLayout (tabs) nested inside
Everything else AppLayout (sidebar, header with breadcrumbs, and global search)

Pages set layout props with a static layout property. App pages provide breadcrumbs:

Index.layout = {
breadcrumbs: [{ title: 'Activity log', href: activity.index() }],
};

Auth pages provide a title and description for the card:

Login.layout = {
title: 'Log in to your account',
description: 'Enter your email and password below to log in',
};

Switching to a top header instead of a sidebar

Section titled “Switching to a top header instead of a sidebar”

resources/js/layouts/app-layout.tsx uses app/app-sidebar-layout.tsx. The kit also ships app/app-header-layout.tsx, which puts navigation in a top bar. Change the import to switch.

resources/js/layouts/auth-layout.tsx uses auth/auth-card-layout.tsx. auth-simple-layout.tsx and auth-split-layout.tsx (with a side panel) are also included. Change the import to switch.

  1. Add a route and controller action:

    Route::get('reports', [ReportController::class, 'index'])->name('reports.index');
    public function index(): Response
    {
    return Inertia::render('reports/index', [
    'reports' => Report::query()->latest()->get(),
    ]);
    }
  2. Create resources/js/pages/reports/index.tsx. It gets AppLayout automatically.

  3. Link to it with the typed route helper (import reports from '@/routes/reports') and add it to the navigation.

For a paginated, searchable list, follow the Data Tables walkthrough instead.

Add a case to the layout switch in app.tsx, the same way welcome returns null.