How does a persistent Inertia layout read the signed-in user from shared props with usePage(), and what does usePage() return?
answer
- the whole page object, not only props
- component, props, url, version
- shared props merged into props
- React: usePage(); Vue: usePage() or $page
- Svelte: page or usePage()
basics
~10 susePage() returns the current Inertia page object, whose props include shared props such as auth.user from HandleInertiaRequests::share(), so a layout reads usePage().props.auth.user and re-renders whenever a visit brings a new page.
solid answer
~40 sEvery Inertia response carries a **page object**: `component`, `props`, `url`, `version` and some metadata. Props the `HandleInertiaRequests` middleware shares, such as `auth.user` or `flash`, are merged into `props` on every response, so a persistent layout that never receives page-specific props can still read them. In React, `const { props } = usePage()` inside the Inertia app tree (it throws outside it); in Vue, `usePage()` returns a reactive object and templates can use `$page.props`; in Svelte, the exported `page` state or `usePage()`. Because the layout reads from the current page, the avatar in a music app's sidebar updates after a visit changes the user, while the layout itself stays mounted. `usePage().url` and `component` suit highlighting the active navigation item.
code
jsx · 19 linesimport { Link, usePage } from '@inertiajs/react'
export default function AppLayout({ children }) {
const { props, url } = usePage()
const user = props.auth.user
return (
<div className="shell">
<aside>
{user && <span>{user.name}</span>}
<Link href="/library" className={url.startsWith('/library') ? 'active' : ''}>
Library
</Link>
{/* persistent audio player lives here */}
</aside>
<main>{children}</main>
</div>
)
}go deeper
Know that usePage().props gives any component the current page's props, including shared ones like the signed-in user.
Explain the page object's fields, how shared props merge into props, and the per-adapter ways to read it, including React's context requirement.
Keep shared props lean and safe, type them once, and rely on usePage in persistent layouts so they update without remounting.
Govern what the app shares globally, balancing convenience against payload size, exposure of data and coupling of every page to global props.
## The page object Every **Inertia** response, whether the first HTML load or a later XHR visit, describes the page as a **page object**: | Field | Example | |---|---| | `component` | `'Albums/Show'` | | `props` | page props plus shared props, including `errors` | | `url` | `'/albums/42'` | | `version` | the asset version string | Some optional metadata, such as which props are deferred or merged, rides along too. The adapter keeps the current page object in a context or store and exposes it through **`usePage()`**. ## Where shared props come from On the Laravel side, `HandleInertiaRequests::share()` returns props added to every response, for example: - `'auth' => ['user' => $request->user()]` - flash messages or feature flags They are merged into `props` beside the page's own props, so every page and every layout can read `props.auth.user` without the controller passing it. ## Reading it in each adapter | Adapter | How | |---|---| | React 19 | `const { props, url } = usePage()` in any component rendered by the Inertia app | | Vue 3 | `const page = usePage()` in `<script setup>`, or `$page.props` in templates | | Svelte 5 | `import { page } from '@inertiajs/svelte'`, or `usePage()` | In React, `usePage()` reads a context through React 19's `use()` and **throws** when called outside the Inertia app tree, such as in a component rendered into a separate root. In Vue, the returned object is reactive, so computed values based on it update automatically. ## Why a persistent layout needs it A persistent layout is declared on the page component and rendered by Inertia around the page. It keeps its instance across visits, which is the point: the sidebar audio player keeps playing. But it should still show current data: 1. the signed-in user's avatar and name from `props.auth.user`; 2. the active navigation item from `url` or `component`; 3. a flash message from shared props after a form redirect. Because the layout reads from `usePage()`, it re-renders when a visit installs a new page object, without remounting. The player state survives; the avatar and active item update. ## Practical rules - Treat `usePage().props` as **read-only**: to change data, make a visit and let the server answer with new props. - Keep shared props small; they travel with **every** response and end up in the browser, so never share secrets or whole models with hidden columns. - Use `url` for matching links, for example marking `/library` active with `url.startsWith('/library')`. - Type shared props once, so `usePage()` knows `auth.user` everywhere. ## Typing shared props once In a TypeScript app, augment Inertia's `InertiaConfig` interface so every `usePage()` call knows the shared shape: ```ts // resources/js/types/global.d.ts import '@inertiajs/core' declare module '@inertiajs/core' { export interface InertiaConfig { sharedPageProps: { auth: { user: { id: number; name: string; avatar_url: string } | null } } } } ``` The `import` line makes the file a module, so the declaration augments Inertia's types instead of replacing them. Page-specific props can still be passed as a generic, `usePage<{ album: Album }>()`, and are combined with the shared ones. ## Common mistakes - passing `auth.user` from every controller instead of sharing it once; - storing the user in layout state at mount and never seeing it update; - calling `usePage()` in React code rendered outside the Inertia root; - mutating `page.props` in the browser and expecting the server to know.
- Why does usePage() throw in a React widget mounted with its own createRoot?The React adapter's `usePage()` reads the page from a context provided by the Inertia `App` component. A separate root is outside that provider, so there is no page to read, and the hook throws instead of returning stale data.
- Why share a trimmed user array instead of the whole model?Shared props are serialised into every response and are readable in the browser, including the initial HTML. Sending only `id`, `name` and `avatar_url` keeps payloads small and avoids leaking columns such as email or internal flags.
saying these in an interview costs you the question
- usePage() returns only the page component's own props
- Shared props must be passed down from the page to the layout
- A persistent layout keeps showing the user from its first mount
- Changing usePage().props in the browser updates the server session
- usePage() makes a request to fetch the current props