In Livewire 4, how do you serve a component as a full page with Route::livewire, and how does #[Layout] choose the page's layout?
answer
- Route::livewire(uri, component)
- pages:: namespace for page components
- component_layout defaults to layouts::app
- layout needs {{ $slot }}
- #[Layout('layouts::dashboard')] on the class
basics
~20 sRoute::livewire('/stock', 'pages::stock.dashboard') registers a GET route that renders the component inside a layout. The layout is config livewire.component_layout (layouts::app) unless #[Layout('...')] on the class or ->layout() in render() overrides it; route parameters reach mount() by name.
solid answer
~40 sIn Livewire 4 you route to a component with `Route::livewire('/warehouses/{warehouse}/stock', 'pages::stock.dashboard')`; it is the preferred form and is **required** for single-file and multi-file page components, while `Route::get('/x', Component::class)` still works for class components. Livewire renders the component into a layout: by default `component_layout` in `config/livewire.php`, `'layouts::app'`, i.e. `resources/views/layouts/app.blade.php`, which must echo `{{ $slot }}` (`php artisan livewire:layout` generates one). `#[Layout('layouts::dashboard')]` on the class, optionally with data such as `['title' => ...]`, overrides it per component, as does `->layout()` on `$this->view()` in `render()`. `#[Title('...')]` or `->title()` fills the layout's `$title`. Route parameters reach `mount()` by name, and a typed model parameter or public property uses route model binding.
code
php · 7 lines<?php
use Illuminate\Support\Facades\Route;
Route::livewire('/warehouses/{warehouse}/stock', 'pages::stock.dashboard')
->middleware('auth')
->name('stock.dashboard');go deeper
Recall Route::livewire with a pages:: name, the default layouts::app layout with {{ $slot }}, and #[Layout] for a different one.
Explain the three ways a layout is chosen, how #[Title] reaches $title, and how route parameters and model binding reach mount().
Plan page components with middleware, named routes and bound models, and catch v3-to-v4 routing and config-key upgrade traps.
Decide when full-page Livewire components replace controllers across an app, and how layouts and page namespaces stay consistent for many teams.
## Components as pages A **page component** is a Livewire component that is the whole response for a URL, instead of being nested in a Blade view. There is no controller: the route points straight at the component, and Livewire wraps the component's HTML in a **layout**. ```php use Illuminate\Support\Facades\Route; Route::livewire('/warehouses/{warehouse}/stock', 'pages::stock.dashboard') ->name('stock.dashboard'); ``` `Route::livewire()` is a route macro Livewire registers. It creates an ordinary `GET` route handled by Livewire's page controller, so everything you can chain on a route (`->name()`, `->middleware()`, `->can()`) still applies. ## Livewire 4 routing rules | Syntax | Status in Livewire 4 | |---|---| | `Route::livewire('/x', 'pages::x')` | Preferred; the name form works for single-file and multi-file components | | `Route::livewire('/x', X::class)` | Preferred for class-based components | | `Route::get('/x', X::class)` | Still works for class components, no longer recommended | `Route::livewire()` is **required** for single-file and multi-file components to work as full pages. The `pages::` namespace points at `resources/views/pages`, and `php artisan make:livewire pages::stock.dashboard` creates the file there, keeping pages apart from reusable components. ## How the layout is chosen 1. **Default**: `component_layout` in `config/livewire.php`, which is `'layouts::app'` in Livewire 4 (it was the `layout` key in v3). That resolves to `resources/views/layouts/app.blade.php`. `php artisan livewire:layout` generates this file if you do not have one. 2. **Per component**: put `#[Layout('layouts::dashboard')]` (from `Livewire\Attributes\Layout`) on the class. A second argument passes data to the layout: `#[Layout('layouts::dashboard', ['title' => 'Stock'])]`. 3. **From render()**: return `$this->view()->layout('layouts::dashboard')` when the choice depends on runtime data. The layout must output the component somewhere. With a Blade component layout that is `{{ $slot }}`; Livewire also supports `@extends`-style layouts, where the content goes into a section. If the layout view does not exist, Livewire throws `MissingLayoutException` with "Livewire page component layout view not found". The layout only matters when the component is rendered as a page; nesting the same component with `<livewire:... />` ignores it. ## Titles and other layout slots - `#[Title('Stock levels')]` on the class, or `->title(...)` on `$this->view()` in `render()`, provides `$title` to the layout, which typically prints `{{ $title ?? config('app.name') }}`. - Named slots of the layout are filled from the component view with `<x-slot:name>` placed **outside** the component's root element. ## Route parameters and model binding Route parameters are handed to `mount()` **by name**: ```php use App\Models\Warehouse; use Livewire\Attributes\Layout; use Livewire\Attributes\Title; use Livewire\Component; new #[Layout('layouts::dashboard')] #[Title('Stock levels')] class extends Component { public Warehouse $warehouse; // bound from {warehouse}, no mount() needed }; ``` A type-hinted `mount(Warehouse $warehouse)` parameter, or a typed public property with the parameter's name, triggers Laravel's **route model binding**, so a missing id returns a 404 before the component runs. A plain `mount($id)` receives the raw string. ## Page component or controller plus nested component? | Concern | `Route::livewire()` page | Controller returning a view with `<livewire:... />` | |---|---|---| | Where route parameters go | `mount()` parameters or typed properties | Controller method, then passed as props | | Layout | `component_layout`, `#[Layout]` or `->layout()` | Whatever the Blade view extends | | Page title | `#[Title]` or `->title()` | Set in the view or layout directly | | Extra work before rendering | `mount()` | Controller code | A page component is the shorter path when the whole page is interactive, such as the stock dashboard. A controller still makes sense when the page is mostly static Blade with a small interactive island, or when the same URL also serves other formats. ## Common mistakes - Using `Route::get()` for a single-file page component in Livewire 4. - A layout without `{{ $slot }}`, so the page renders empty. - Putting `#[Layout]` on a nested component and expecting it to change anything. - Still configuring the v3 `layout` key after upgrading; the v4 key is `component_layout`.
- In a Livewire page component, how do you set the page title from a property value?Use the fluent form in `render()`: `return $this->view()->title('Stock: '.$this->warehouse->name);`. The `#[Title('...')]` attribute only takes a constant string. The layout must print `$title`, typically as `{{ $title ?? config('app.name') }}`.
- Why can Laravel middleware such as can:view,warehouse see the model on a Route::livewire route?Livewire resolves implicit route bindings for page components using the component's `mount()` parameters or typed properties, so the route has a bound `Warehouse` before middleware runs. Authorization middleware and `->can()` therefore work as they would on a controller route.
saying these in an interview costs you the question
- Route::get() is required for Livewire 4 single-file page components
- #[Layout] changes the layout of a component nested in another view
- Livewire 4 still reads the v3 layout config key
- The layout receives the component's HTML automatically without {{ $slot }}
- Route parameters are ignored unless you read them from request()