In Laravel, how do Blade, Livewire, Inertia and a separate SPA on a JSON API differ in routing ownership and where UI state lives?
answer
- who writes the URL table
- Inertia keeps Laravel routes and controllers
- Livewire state round-trips in a snapshot
- a separate SPA needs a client router
- one repository or two
basics
~20 sBlade, Livewire and Inertia all keep routing in Laravel's route files and controllers; only a separate SPA moves routing into a client-side router over a JSON API. UI state lives in the server render (Blade), a component snapshot (Livewire), or client components (Inertia, SPA).
solid answer
~50 sWith **Blade-only** pages, `routes/web.php` maps URLs to controllers that return views, and each interaction is a new full page, so state lives in the database, session and query string. **Livewire** keeps the same routes, but components hold public properties whose values travel to the browser and back in a signed snapshot on every update — state is serialized, not kept in server memory. **Inertia** keeps Laravel's routes and controllers too; a controller returns `Inertia::render('page', $props)` and the React, Vue or Svelte page holds client-side state, with navigation done by XHR requests that return the next page's props. A **separate SPA** owns routing in its own router, calls a JSON API (usually `routes/api.php` with API resources), and holds all UI state in the browser — with a second repository, a second deploy and an API contract to version.
code
php · 15 lines<?php
use App\Models\Booking;
use Illuminate\Support\Facades\Route;
use Inertia\Inertia;
// Blade: Laravel owns the URL and returns full HTML
Route::get('/bookings', fn () => view('bookings.index', [
'bookings' => Booking::latest()->paginate(),
]))->middleware('auth');
// Inertia: Laravel still owns the URL; the page component gets props
Route::get('/calendar', fn () => Inertia::render('calendar', [
'bookings' => Booking::upcoming()->get(),
]))->middleware('auth');go deeper
Remember that Blade, Livewire and Inertia all use routes/web.php and controllers, while a separate SPA has its own router and calls an API.
Explain where state lives in each: new HTML per request, a round-tripped snapshot, client-side props, or a client store over an API.
Tie routing ownership to security and delivery: per-route middleware versus per-endpoint API checks, one deploy versus two.
Frame the choice as how many clients the backend must serve and how many codebases the team can sustain.
## The four shapes Laravel's docs frame the frontend choice as **PHP-first** (Blade, optionally with Livewire) versus **JavaScript-first** (React, Vue or Svelte, joined to Laravel through Inertia). A fourth shape sits outside that framing: a completely **separate SPA or mobile client** talking to Laravel over a JSON API. The two questions that separate them most cleanly are *who decides what a URL means* and *where the data for the current screen lives between interactions*. ## Routing ownership | Approach | Where routes are defined | What a controller returns | Client router? | |---|---|---|---| | Blade-only | `routes/web.php` | `view('orders.index', ...)` — full HTML | none | | Livewire | `routes/web.php` (routes may point straight at a component) | a page containing components | optional SPA-style navigation via `wire:navigate` | | Inertia | `routes/web.php` | `Inertia::render('orders/index', $props)` | Inertia's client visits, but URLs still come from Laravel | | Separate SPA | the SPA's own router | JSON from `routes/api.php`, usually through API resources | yes, fully client-owned | The first three share a property that matters in practice: **Laravel is the single source of truth for URLs, middleware and authorization per route**. A route's `auth` or `can:` middleware protects the page itself. With a separate SPA, the client decides which screen to show, and the API must protect each endpoint independently — the client's route guards are only UX. ## Where state lives - **Blade-only**: effectively stateless screens. Every form post or link click fetches a new HTML document; anything that must survive goes in the database, the session, flash data or the query string. - **Livewire**: a component's public properties are rendered into HTML, and on each interaction the browser sends a **snapshot** of that state back with the action; the server rehydrates the component, runs the action, re-renders and returns a new snapshot. A checksum stops tampering, and properties marked `#[Locked]` cannot be changed from the browser. Nothing stays in server memory between requests. - **Inertia**: props arrive as JSON with each visit, then live in the page component's client state. Local interactions (toggles, tabs, a drag in a calendar) cost nothing on the server until you submit a form or visit another page. - **Separate SPA**: all UI state is client-side, usually with a store and a data-fetching cache; the server keeps only data and authentication. ## Repository and contract - **Blade, Livewire, Inertia**: one repository, one deploy, no public API to version. Changing a prop is a same-commit change on both sides. - **Separate SPA**: two codebases, two build pipelines and an API contract. Changes need coordination or versioning — the cost Laravel's own frontend docs cite as the reason Inertia exists. ## Choosing, briefly 1. Mostly forms and tables, PHP team → Blade, adding Livewire where screens need interactivity. 2. Rich client interaction, a team fluent in React, Vue or Svelte, but no separate API consumers → Inertia. 3. Several clients (web plus mobile, or third parties) must share one backend → an API is needed anyway; a separate SPA becomes reasonable, or Inertia for the web beside an API for mobile. ## Mixing approaches These are not exclusive choices inside one application. Because Blade, Livewire and Inertia all answer ordinary routes in `routes/web.php`, a single app can serve: - Blade pages for marketing and documentation; - Livewire components for admin CRUD screens; - Inertia pages for the one or two screens that need a rich JavaScript UI; - a `routes/api.php` for a mobile client. What is hard to mix is a **separate SPA** with the others for the same screens, because it moves routing out of Laravel entirely. Teams usually pick one primary approach for consistency and allow exceptions deliberately. ## Traps interviewers listen for - Saying Inertia "needs an API" — it does not; controllers pass props directly. - Saying Livewire "keeps components alive on the server" — state round-trips with every request. - Treating client-side route guards in an SPA as security.
- With Inertia, where do authorization checks for a page belong?On the Laravel route or controller, exactly as with Blade: `auth` and `can:` middleware, policies or `Gate` checks run before `Inertia::render` is called. The React, Vue or Svelte page only receives props the controller chose to send, so hiding a button in the page is UX, not protection.
- Why does a Livewire component's state survive between clicks if the server keeps nothing in memory?Livewire serializes the component's public properties into a snapshot embedded in the page. Each interaction posts that snapshot back with the action; the server rebuilds the component from it, runs the method, re-renders and returns a new snapshot. A checksum rejects snapshots altered in the browser.
Blade is a restaurant that brings a fresh plate for every order. Livewire is a waiter who carries your whole order slip back to the kitchen every time you change one item. Inertia hands you a menu you can rearrange at the table, but the kitchen still decides what is on it. A separate SPA is a takeaway: you run your own table and phone the kitchen for each dish.
saying these in an interview costs you the question
- Inertia requires building a REST API for every page
- Livewire keeps each component instance in server memory between requests
- A separate SPA's route guards are enough to protect data
- Livewire replaces Laravel's routes with its own client-side router
- Blade-only apps cannot keep any state between requests