When it comes to integrating React with a Laravel backend, the automatic answer is often: "you build a REST API". Inertia.js offers a third way — and it deserves a closer look.
The problem Inertia solves
Imagine a project creation form. In classic API mode, here is what happens:
- The frontend submits the data in JSON format
- The backend validates and returns either validation errors or the created object
- The frontend intercepts the response, updates the state, manages validation errors field by field, and redirects on success
It works. But that's code you have to write, maintain, and test on both sides — often just to replicate what Laravel does out of the box.
Inertia solves this by keeping the backend as the single source of truth for data and navigation, while letting React handle rendering the user interface.
How it works in practice
Inertia intercepts link clicks and form submissions. Instead of reloading the page, it sends an XHR request with a special header. The server recognizes this header and returns only the component's data (props), not the full HTML.
The client receives this data and re-renders only the relevant component. Visually, it feels like an SPA. Under the hood, it's classic server-side routing without the trade-offs.
import { useForm } from '@inertiajs/react';
export default function CreateProject() {
const { data, setData, post, processing, errors } = useForm({
name: '',
description: '',
});
const submit = (e: React.FormEvent) => {
e.preventDefault();
post('/projects'); // Inertia handles the request and errors
};
return (
<form onSubmit={submit} className="space-y-4">
<input
value={data.name}
onChange={e => setData('name', e.target.value)}
className="w-full rounded border p-2"
/>
{errors.name && <p className="text-red-500 text-sm">{errors.name}</p>}
<button disabled={processing} className="bg-indigo-600 text-white px-4 py-2 rounded">
Create
</button>
</form>
);
}Laravel validation errors are passed directly to errors without manual mapping. The server-side redirect (return Redirect::route('projects.index')) is respected on the client. Local state (scroll position, open modals) is preserved between page transitions.
What you actually gain
A single source of authentication
No JWT tokens to manage on the frontend, no session synchronization. Laravel Sanctum + cookies, and everything just works.
Laravel Form Requests remain central
Your validation logic lives in one place. Complex rules — like checking if a user has reached their project limit — stay on the server where they have database access.
Less total boilerplate code
On a medium-sized project, removing the API layer easily cuts out 30% to 40% of boilerplate code — API controllers, JSON resources, and duplicate frontend types.
Fluid SPA experience
The client navigates instantly without full page reloads, offering the responsiveness of a modern client-side application out of the box.
What you lose
We must be honest about the trade-offs.
Keep architectural trade-offs in mind:
- No public API by default: If your project needs to expose its data to mobile apps or third-party partners, you will still need to build separate endpoints.
- More complex SSR: Server-Side Rendering with Inertia exists but requires extra setup and running a separate Node process alongside Laravel.
- Tight coupling: The frontend and backend live in the same repository. For teams that want a strict separation of concerns, this can create organizational friction.
My verdict
Key takeaway:
Inertia is the best choice for web products built by tight, full-stack teams. It eliminates an entire category of boilerplate code and network issues without introducing major technical constraints.
If you are building a SaaS, an internal tool, or a business application with Laravel and React, Inertia is seriously worth considering.