Stu Mason
Stu Mason
Guide

Inertia.js vs SPA vs Livewire: When to Use What

Stuart Mason7 min read

Let me save you some time: if you're building a Laravel application with a modern UI, use Inertia.js. That's my answer for about 80% of projects. But the other 20% matters, so let's actually talk abou

Inertia.js vs SPA vs Livewire: When to Use What

Let me save you some time: if you're building a Laravel application with a modern UI, use Inertia.js. That's my answer for about 80% of projects. But the other 20% matters, so let's actually talk about this properly.

What Are We Actually Comparing?

Inertia.js is a routing layer that lets you build server-rendered apps using React, Vue, or Svelte for the frontend. No API required. Your Laravel controllers return Inertia responses instead of Blade views, and the frontend renders them as a single-page app. You get the DX of an SPA with the simplicity of a traditional server-side app.

Full SPA means your Laravel backend is purely an API (usually REST or GraphQL), and your frontend is a completely separate application — typically React with Next.js or Vue with Nuxt. Two deployments, two codebases (or a monorepo), two sets of concerns.

Livewire is a full-stack framework for Laravel that makes building dynamic interfaces without writing JavaScript. You write PHP components that handle frontend interactions through WebSocket-like communication. It's the "I don't want to learn React" option, and honestly, it's quite good at what it does.

Inertia.js: The Sweet Spot

I've used Inertia on every project I've built for the last few years. TidyLinker — a marketplace platform with Stripe Connect, real-time features, and complex user flows — is built on Inertia. The Progress site you're reading right now is Inertia v2 with React 19.

Here's why:

One codebase, one deployment. Your routes are Laravel routes. Your controllers are Laravel controllers. Your authentication is Laravel authentication. You don't need an API layer, you don't need token management, you don't need CORS configuration. The amount of shit that just disappears is significant.

// This is your entire "API" for a page
class DashboardController extends Controller
{
    public function index(): Response
    {
        return Inertia::render('Dashboard/Index', [
            'stats' => DashboardData::fromUser(auth()->user()),
            'recentActivity' => ActivityData::collection(
                auth()->user()->activities()->latest()->limit(10)->get()
            ),
        ]);
    }
}

Inertia v2 is genuinely excellent. Deferred props mean you can lazy-load expensive data. Polling is built in. Infinite scrolling works with merging props. The <Form> component handles form state, errors, and submission. It's not a toy — it's a mature framework.

// Deferred props load after the page renders
<Deferred fallback={<SkeletonLoader />}>
    <ExpensiveDataComponent data={expensiveData} />
</Deferred>

You get full React/Vue/Svelte. This isn't Blade with extra steps. You're writing proper React components with hooks, context, the whole lot. TypeScript works perfectly. With Wayfinder generating route types from your Laravel controllers, you get end-to-end type safety.

When Inertia is the wrong choice:

  • You need a mobile app that shares the same API (you'll need that API anyway)
  • You're building something that needs to work offline
  • Your frontend team and backend team are completely separate organisations
  • You're building a static marketing site (just use Next.js or Astro)

Full SPA: When You Actually Need One

A full SPA — React frontend, Laravel API backend — is the right choice when:

You need the API for other consumers. If you're building a mobile app alongside your web app, you need that API anyway. Having a separate SPA frontend is natural because the API is doing double duty. Rezzy is a good example — the restaurant widget needed its own frontend that could be embedded anywhere, so a proper API was essential.

Your team structure demands it. If you've got a frontend team in one timezone and a backend team in another, and they deploy independently, a full SPA with a well-documented API makes collaboration smoother.

But here's what nobody tells you about full SPAs:

Authentication is a pain in the arse. Token management, refresh tokens, token storage, CSRF for cookie-based auth, CORS configuration — it's all solvable, but it's friction you don't need if Inertia would work. I've lost days to auth issues on SPA projects. Days.

You build everything twice. Loading states, error handling, data fetching, caching, optimistic updates — all of this is your problem now. Inertia handles most of it automatically. With a full SPA, you're reaching for React Query or SWR and writing custom hooks for every endpoint.

SEO requires SSR. If you need search engine indexing (and most apps don't, to be honest — your marketing site does, your app doesn't), you need server-side rendering, which means Next.js or Nuxt, which means a Node server, which means another deployment target.

// This is what "simple" data fetching looks like in a full SPA
const { data, isLoading, error, refetch } = useQuery({
    queryKey: ['dashboard'],
    queryFn: () => api.get('/api/dashboard').then(r => r.data),
    staleTime: 30_000,
});

// vs Inertia where the data is just... there
// as a prop on your page component

Livewire: The PHP Developer's Escape Hatch

Livewire is brilliant if you hate JavaScript. I mean that genuinely, not as a dig. If you're a PHP developer who finds React overwhelming, Livewire lets you build interactive UIs without leaving PHP.

When Livewire shines:

  • Admin panels and internal tools where DX speed matters more than pixel-perfect UI
  • CRUD-heavy applications where most interactions are form submissions
  • Teams that are strong in PHP but weak in JavaScript
  • Rapid prototyping where you need something working in hours, not days
  • Projects already using Blade where you need to add interactivity incrementally

When Livewire struggles:

  • Complex, highly interactive UIs (drag-and-drop, real-time collaboration, rich text editing)
  • Offline-capable applications
  • Performance-critical frontends where round trips to the server matter
  • Projects where you want to hire React developers (the talent pool is massive)

The elephant in the room is that Livewire requires a round trip to the server for every interaction. It's fast enough for most things — don't let anyone tell you it's slow — but there's a fundamental latency floor you can't get below. For a button click that updates a counter, you won't notice. For a drag-and-drop interface with 60fps requirements, it's the wrong tool.

My Decision Framework

Here's how I decide:

  1. Is this a Laravel app with a web frontend? → Inertia.js
  2. Does this need a mobile app or multiple frontend consumers? → Full SPA + API
  3. Is the team PHP-only and the UI is mostly forms and tables? → Livewire
  4. Is this a marketing site or blog? → Next.js or Astro (don't use Laravel at all)

That's it. No flowchart needed.

The Real Reason I Use Inertia

Velocity. I ship faster with Inertia than with any other approach. One php artisan make:controller, one React component, one route definition, and I've got a working page. No API versioning, no token management, no separate deployment pipeline.

On both TidyLinker and Progress, I can go from "this feature doesn't exist" to "it's in production" in hours, not days. That matters when you're a freelancer and clients are paying for your time.

The stack on this site — Laravel 12, Inertia v2, React 19, TypeScript, Tailwind v4, Wayfinder — is the most productive web development setup I've used in 16 years. And I've used a lot of setups.

Pick the tool that matches your constraints. But if your constraint is "I need to build a web app quickly and well," Inertia is the answer.


I write about Laravel, AI tooling, and the realities of building software. More at stuartmason.co.uk.

Get the Friday email

What I shipped this week, what I learned, one useful thing.

No spam. Unsubscribe anytime. Privacy policy.