Skip to content
Muhammet Şafak
tr
Framework & Library 3 min read

From monolith to modern UI with Inertia.js

How Inertia.js delivers a single-page-app feel without a separate API, how it integrates with Laravel, and when it's the right choice.


When you want to move a monolithic Laravel application toward a modern frontend, it can seem like you only have two options: stay with Blade templates, or write a separate SPA and wire it up through an API. The first is the familiar path; the second is expensive — two separate codebases, two separate authentication mechanisms, API versioning decisions, and deployment complexity.

Inertia.js offers a third way between these two extremes. The monolith stays, but the UI is written in Vue or React. No need to design an API.

How Inertia works

Inertia intercepts page transitions. When you click a normal link, the browser sends a request to the server; Inertia catches that request and receives the page component and its data in JSON format. There’s no full page reload — the Vue component is updated in place.

On the server side, a single Inertia::render() call is all you need:

// app/Http/Controllers/PostController.php
use Inertia\Inertia;

class PostController extends Controller
{
    public function index()
    {
        $posts = Post::with('user')
            ->latest()
            ->paginate(15);

        return Inertia::render('Posts/Index', [
            'posts' => $posts,
        ]);
    }

    public function show(Post $post)
    {
        return Inertia::render('Posts/Show', [
            'post' => $post->load('comments.user'),
        ]);
    }
}

On the client side, Posts/Index is a Vue component that receives posts as a prop:

<template>
  <div>
    <article v-for="post in posts.data" :key="post.id">
      <h2>{{ post.title }}</h2>
      <p>{{ post.user.name }}</p>
    </article>

    <Pagination :links="posts.links" />
  </div>
</template>

<script>
export default {
  props: {
    posts: Object,
  },
};
</script>

Authentication, middleware, sessions — all of it is handled by Laravel’s own mechanisms. There’s nothing extra to set up.

You don’t have to write an API — but that’s also a constraint

Inertia’s “no API” model leaves certain scenarios out of scope. If the same API needs to be consumed by a mobile app or a third-party client, Inertia isn’t the right fit — you’ll need a proper separate API for that. Inertia is designed for a single client that shares the same Laravel application as the web interface.

There’s another trade-off: the server and client are tightly coupled. Component names and prop structures are defined in Laravel controllers. The frontend can’t evolve independently. For some teams that’s perfectly fine; for others, it’s a dealbreaker.

Passing global data with shared data

Data that repeats on every page — the authenticated user, flash messages, global settings — shouldn’t be passed individually from every controller. Inertia’s share() mechanism solves this:

// app/Http/Middleware/HandleInertiaRequests.php
public function share(Request $request): array
{
    return array_merge(parent::share($request), [
        'auth' => [
            'user' => $request->user(),
        ],
        'flash' => [
            'success' => $request->session()->get('success'),
            'error'   => $request->session()->get('error'),
        ],
    ]);
}

This data is automatically included in every Inertia response and is accessible in components via $page.props.auth.user. Think of it as the Inertia equivalent of View::share() in Laravel.

Migration strategy: from Blade to Inertia

It’s possible to move an existing Laravel + Blade project to Inertia without rewriting everything at once. The two systems can run in parallel: some routes return Blade views while others return Inertia::render. That’s key for a gradual migration.

Starting with a single screen, seeing how Inertia behaves there, and giving the team time to get comfortable is a much safer approach. Migrating the entire application in one day is unnecessary risk.

Comparison with Livewire

This year I used both Livewire and Inertia on real projects. The difference comes down to this:

Livewire is designed for backend-focused teams — minimal JavaScript, server-side rendering dominant. Inertia is a better fit when a frontend developer is in the picture — you get the full power of Vue or React.

If the project is a form-heavy admin panel, Livewire lets me write fewer components. If the project involves complex client-side interactions, or there’s someone on the team who can write Vue, Inertia feels more natural.


Inertia takes a sharp position in the monolith-vs-SPA debate: not at scale, not for multiple clients, but for the single-client scenario it strikes a perfect balance. It’s exactly the right tool at the point where designing an API feels like unnecessary overhead, but Blade is no longer enough.

Share:

Comments

Sign in with your GitHub account to join the discussion. Comments are stored in GitHub Discussions.

Related Posts

Search the site

Start typing to search posts, projects and pages.

Esc to close Powered by Pagefind