skip to content

How does a persistent Inertia layout read the signed-in user from shared props with usePage(), and what does usePage() return?

level: juniorimportance: should knowfreq 42%

answer

  1. the whole page object, not only props
  2. component, props, url, version
  3. shared props merged into props
  4. React: usePage(); Vue: usePage() or $page
  5. Svelte: page or usePage()

basics

~10 s

usePage() 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 s

Every 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 lines
jsx
import { 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

for a junior

Know that usePage().props gives any component the current page's props, including shared ones like the signed-in user.

for a middle

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.

for a senior

Keep shared props lean and safe, type them once, and rely on usePage in persistent layouts so they update without remounting.

for a principal

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