In a Laravel app using Inertia 3, what must the root Blade template contain, and how do you choose a different root view per area?
answer
- loaded only on the first visit
- x-inertia::head with a fallback slot
- x-inertia::app or @inertia mounts the app
- protected $rootView = 'app'
- rootView(Request), setRootView, ->rootView()
basics
~10 sThe root view needs the Vite assets, x-inertia::head (or @inertiaHead) and <x-inertia::app /> (or @inertia). Its name defaults to app; change it with $rootView or rootView(Request) in HandleInertiaRequests, Inertia::setRootView, or ->rootView() on one response.
solid answer
~40 sThe root view is the Blade document Inertia renders only on a full page load. It must load the bundle with `@vite`, print head tags with `<x-inertia::head>`, whose slot is a fallback used when SSR is not active, and mount the app with `<x-inertia::app />`, which prints the page-object script and `<div id="app">`. The `@inertiaHead` and `@inertia` directives still work but lack the head fallback. The page object is also available there as `$page`. The name defaults to `app`: set `protected $rootView` in `HandleInertiaRequests`, override its `rootView(Request $request)` to choose per request (say, `owner` for an owner portal), call `Inertia::setRootView()`, or chain `->rootView('owner')` onto one `Inertia::render()`. Because client-side visits never re-render the root view, moving between two shells needs a full page load.
code
html · 15 lines<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
@viteReactRefresh
@vite(['resources/js/app.tsx', "resources/js/pages/{$page['component']}.tsx"])
<x-inertia::head>
<title>{{ config('app.name') }}</title>
</x-inertia::head>
</head>
<body>
<x-inertia::app />
</body>
</html>go deeper
Recall that app.blade.php loads the assets and mounts the app with <x-inertia::app /> or @inertia, and that it renders only on the first load.
Explain the head fallback slot, the $page variable in the root view, and the four ways to pick a root view from middleware down to one response.
Anticipate the two-shell trap and the stale view data on client visits, and route users between shells with full navigations.
Weigh one root view with client layouts against several root views with separate bundles, considering caching and how often users cross areas.
## What the root view is An Inertia app still has exactly one Blade file that matters: the **root view** (also called the root template). The adapter renders it for requests **without** the `X-Inertia` header, meaning the first visit, a refresh or a pasted link. Every later client-side visit returns JSON and leaves the document the root view produced in place. The name defaults to `app`, i.e. `resources/views/app.blade.php`. Both `Inertia\ResponseFactory` and the published `HandleInertiaRequests` middleware start with `protected $rootView = 'app'`. ## What it must contain A working root view has four jobs: 1. **Load the assets** with `@vite([...])`; React apps add `@viteReactRefresh` before it for Fast Refresh in development. 2. **Print head content** with `<x-inertia::head>`. When SSR rendered the page it prints the head tags SSR produced; otherwise it prints whatever you put in its slot, such as a default `<title>`. 3. **Mount the app** with `<x-inertia::app />`. Without SSR it prints `<script data-page="app" type="application/json">` holding the page object and `<div id="app"></div>`; with SSR it prints the server-rendered markup instead. Pass `id="..."` to rename the element, and keep the client-side setup in step. 4. **Use `$page` if needed.** The page object is passed to the view as `$page`. The React starter kit, for example, adds `resources/js/pages/{$page['component']}.tsx` to its `@vite` call so the current page's chunk is preloaded. | Inertia 3 Blade component | Older directive | Difference | |---|---|---| | `<x-inertia::head>` | `@inertiaHead` | only the component has a fallback slot for non-SSR responses | | `<x-inertia::app />` | `@inertia` | same output; both accept a custom element id | The components arrived in Inertia 3; the directives remain supported, and the docs recommend the components for new apps. ## Choosing a different root view There are four levers, from broad to narrow: - `protected $rootView = 'owner';` in `HandleInertiaRequests` changes the default for every request through that middleware. - Override `rootView(Request $request)` in the same middleware to decide per request, for example `$request->routeIs('owner.*') ? 'owner' : 'app'`. The middleware passes its answer to `Inertia::setRootView()` before the controller runs. - Call `Inertia::setRootView('owner')` yourself, for instance in a route-group middleware. - Chain `->rootView('owner')` onto a single `Inertia::render(...)` response. ## Passing data to Blade, not to the page `Inertia::render(...)->withViewData(['metaDescription' => $text])` adds variables for the root view only. They never enter `props`, so the page component cannot read them, and they are only seen on full loads, because client-side visits never render the root view. Use them for things the first HTML needs, such as a `<meta>` tag or a `lang` attribute. ## The trap: two shells, one session Suppose the tenant dashboard uses `app` and a separate owner portal uses `owner`, with different CSS bundles. A `<Link>` from the dashboard to an owner route returns JSON; the browser keeps the `app` document and its assets, and the owner page renders inside the wrong shell. Anything the root view alone provides is missing until the next full load. The fix is to cross between shells with a real navigation: a plain `<a href>`, or `Inertia::location()` from the server. ## When the first load is blank A root view mistake usually shows up as an empty page with no error on the server. Work through it in order: 1. View the page source and look for the `<script data-page="app" type="application/json">` element. If it is missing, the root view lacks `<x-inertia::app />` or `@inertia`, or a different view is being rendered. 2. Check that the element id matches the one the client mounts on; a custom `id` on the component must be mirrored in the client setup. 3. Check that `@vite` points at the real entry file and that the dev server or the build manifest is available. 4. If the head shows two titles under SSR, move the default title into the slot of `<x-inertia::head>`; that duplicate-title problem is what the fallback slot was introduced to solve. ## Inertia 3 notes - `<title inertia>` in the root view became `<title data-inertia>`. - The page object is always embedded in a script element; the old `data-page` attribute on the `<div>` is gone. - Run `php artisan view:clear` after upgrading, since the directive output changed.
- Why can a <Link> into an area with a different root view render inside the wrong shell?A client-side visit returns only the page object as JSON. The browser keeps the document the first root view produced, with its CSS, scripts and head, and the new component renders inside it. The other root view is applied only on the next full load, so crossing between shells should use a plain anchor or a server-side `Inertia::location()`.
- When would you use withViewData() instead of a prop?When the value is for the Blade root view itself, such as a meta description, a `lang` attribute or a theme class on `<html>`. `withViewData()` data never reaches `props`, so the page component cannot read it, and it only applies on full loads. Anything the page component renders or updates on later visits belongs in props.
saying these in an interview costs you the question
- The root view is rendered again on every Inertia visit.
- Inertia 3 removed the @inertia and @inertiaHead directives.
- config/inertia.php has a root_view key for choosing the template.
- withViewData() values are merged into the page props.
- A <Link> into another root view swaps the whole HTML shell.