# 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.

- Published: 2020-09-06
- Category: Framework & Library
- Tags: Laravel, Inertia.js
- Reading time: 3 min read
- Source: https://www.muhammetsafak.com.tr/en/blog/from-monolith-to-modern-ui-with-inertia-js/
- Language: en-US
- Author: Muhammet Şafak

---
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](/en/blog/thinking-in-components-with-laravel-5-4-and-blade/), or write a separate [SPA and wire it up through an API](/en/blog/writing-json-api-endpoints-with-laravel/). 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:

```php
// 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:

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

    <Pagination :prev="posts.prev_page_url" :next="posts.next_page_url" />
  </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:

```php
// app/Http/Middleware/ShareInertiaData.php
public function handle(Request $request, Closure $next)
{
    Inertia::share([
        'auth' => [
            'user' => $request->user(),
        ],
        'flash' => [
            'success' => $request->session()->get('success'),
            'error'   => $request->session()->get('error'),
        ],
    ]);

    return $next($request);
}
```

This data is automatically included in every Inertia response and is accessible in components via `$page.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.
