The Modern CMS Stack: Laravel + React + Inertia
One deployable artifact, type safety end-to-end, no API to design — and the trade-offs you should know before picking it.
Most CMS architecture conversations in 2026 default to "headless Node + Next.js + a SaaS for content," and that's the right answer for some teams. For a lot of others — agencies, small product teams, monolithic-by-default shops — it's a 4-service architecture solving a 1-service problem.
This post is the case for the monolithic modern CMS stack: Laravel 12 + React 19 + Inertia 2 + shadcn/ui + Tailwind v4 — one deployable artifact, one repo, one server, one database. TL;DR: this stack gives you React components for the admin and frontend with TypeScript end-to-end, while keeping the operational shape of a single Laravel app. You skip the headless API design, the separate frontend deployment, and the auth-token shuttling. The trade-off is real: you can't drop a separate Next.js or Vite frontend on top because the integration is via Inertia, not REST. For teams where that's the right shape, this stack is one of the fastest ways to ship a serious CMS in 2026. See also: the long-form developer's guide to modern CMS selection.
We run this exact stack — UnfoldCMS is built on it. This isn't a neutral architecture overview; it's the working notes from shipping a real production CMS on it. The numbers we cite are ours.
The Architecture Question Most Teams Skip
Before picking a CMS, there's an architectural question most teams answer by default instead of by decision: monolith or headless?
The default answer for the last 5 years has been "headless." The reasoning was real:
- Decouple frontend from backend so they scale independently
- Use the best frontend framework regardless of CMS language
- API becomes the integration point so multiple frontends (web, mobile, kiosk) share content
- Frontend team and backend team work in different repos with different deploys
That reasoning is sound for the projects it was designed for: large content operations with multiple consumer surfaces, large frontend teams, content shared across products. For a 3-person team building a marketing site + blog + product pages, all of that decoupling becomes overhead.
The honest cost of headless for a small-to-medium team:
- Two deployments (frontend + backend) instead of one
- Two CI pipelines, two monitoring stacks, two log streams
- Auth tokens shuttled between services on every request
- Schema changes coordinated across two repos and two release cycles
- "Why is the staging frontend showing prod data" debugging sessions
Monolithic CMSes solve a different shape of problem. The trade-off is honest: you give up the ability to drop a different frontend on top of the same content (no Next.js + Vue mobile app + iOS app reading the same API), and you commit to the CMS's stack as your stack.
For teams where that commitment is fine, the monolithic modern CMS stack is genuinely faster, simpler, and cheaper to operate. The 2026 question isn't "monolith or headless" — it's "which one fits this project."
What "Modern CMS Stack" Means Here
By "modern CMS stack" we don't mean WordPress + a React theme. We mean the specific shape of:
- Backend: Laravel 12 (PHP 8.3+), Eloquent ORM, type-safe via Spatie Laravel Data
- Frontend: React 19 + TypeScript, with shadcn/ui as the component primitive
- Glue: Inertia 2 — server-side routing, React components rendered with server-fetched props
- Styling: Tailwind v4 (CSS-first, theme via CSS variables)
- Database: MySQL 8 or Postgres
- Deployment: one server, one repo, one
php artisan deploycommand
This is the stack we built UnfoldCMS on. 205 admin pages, 51 shadcn/ui components in production, three themes via CSS variables. The whole thing deploys as a single Laravel app.
Quick comparison: the four shapes a modern CMS can take in 2026:
| Shape | Example | Frontend integration | Operational surface |
|---|---|---|---|
| Headless SaaS | Sanity, Contentful | REST/GraphQL → separate Next.js | 2 services + SaaS billing |
| Headless self-hosted | Strapi, Directus | REST/GraphQL → separate Next.js | 2 services (CMS + frontend) |
| Hybrid Next.js | Payload v3 | Local API → same Next.js app | 1 Next.js + Postgres |
| Monolithic Laravel + React | UnfoldCMS, Statamic + Inertia | Inertia (no API) → same Laravel app | 1 Laravel + MySQL |
The Payload v3 and the Laravel+Inertia shapes are both "monolithic modern" — single deployable, fully typed end-to-end. They differ on language (TypeScript-only Node vs PHP+TypeScript) and runtime (Next.js vs Laravel).
For more on this category split see our WordPress vs modern CMS comparison and the best CMS for React developers post that ranks each option.
What Inertia Actually Is (And Why It Matters)
Inertia gets misunderstood. Most React developers hear "server-rendered React" and assume Next.js — they're not the same shape.
Next.js model: The Next.js app is the runtime. It serves pages, runs API routes, handles routing. Your data layer (a CMS, a database, an auth provider) sits behind it as services. RSC and Server Actions added in-process work but the architectural unit is "the Next.js app."
Inertia model: The Laravel app is the runtime. It handles routing (Route::get('/posts', PostController::class)), fetches data, runs auth, builds full server-side responses. Each route returns a response that says "render this React component with these props." Inertia ships those props to the browser as JSON, React renders the component, and you get a full SPA experience without writing an API.
The mental model:
// Laravel route — server-side
Route::get('/posts', function () {
return Inertia::render('Posts/Index', [
'posts' => Post::published()->paginate(20),
]);
});
// React component — client-side
export default function PostsIndex({ posts }) {
return <PostList posts={posts} />;
}
There's no REST endpoint. No fetch call. No auth token. The server renders the response, ships JSON props, and React picks up where the server left off. Subsequent navigation (<Link href="/posts/123">) does a fetch to the same controller, which returns JSON props instead of full HTML, and the React component swaps in.
What this means in practice:
- No API surface to design — the contract between frontend and backend is "what props does this React component expect?" not "what does this REST endpoint return?"
- Auth is shared — Laravel session = React user. No JWTs, no refresh tokens, no Authorization headers.
- Form submissions go to controllers — a
<form>POSTs to a Laravel route, which validates with FormRequest or Spatie Data, and returns the next page or validation errors. Same UX as an API; less code. - Type safety is real —
php artisan typescript:transformgenerates TypeScript types from PHP Data classes; React components consume the same types the Laravel controller produces.
The Inertia model is what makes the monolithic Laravel + React stack feel like a SPA without the SPA's operational cost. It's the load-bearing piece of why this stack works.
Single Deployable Artifact: The Operational Story
The biggest practical win of the monolithic modern CMS stack is operational simplicity. Here's what production looks like:
One repo: the entire app lives in one git repo. Backend and frontend share the same commit history. No "frontend PR depends on backend PR" coordination.
One deployment: php artisan deploy runs locally, pushes to GitHub, the server pulls, runs migrations, builds the React bundle locally, and rsyncs the assets. The whole sequence is a single command. No separate frontend CI pipeline, no separate Vercel deployment, no environment-variable drift between two services.
One server: Laravel runs on Nginx + PHP-FPM. The React bundle is static assets served by the same Nginx. Database is MySQL on the same VPS. Total: one $20/month VPS handles a real production CMS comfortably.
One log stream: tail -f storage/logs/laravel.log. There isn't a "frontend logs" and "backend logs" to correlate when debugging.
One auth model: Laravel session middleware authenticates the user. Inertia ships the authenticated user as a prop. React reads it. No "is the user logged in on the API too?" question to ask.
For a small team, this collapses an entire category of operational work. No coordinating two deploys, no handling a half-deployed state where the frontend has a feature the backend doesn't, no "the staging frontend points at the wrong API" incidents. The cost is the commitment to Laravel as your runtime — which for Laravel-comfortable teams is a non-cost.
The same shape exists in Payload v3 (Next.js + in-process Local API) and Inertia stacks beyond ours (Statamic now ships with Inertia support, Rails has Inertia.js, Django too). Single deployable is the architectural pattern; the language is the variable.
Type Safety End-to-End: How the Pieces Connect
The Laravel + React + Inertia stack achieves end-to-end type safety through three pieces:
1. Spatie Laravel Data for typed PHP data structures:
class PostData extends Data
{
public function __construct(
public string $title,
public string $slug,
public ?string $excerpt,
public Carbon $published_at,
public CategoryData $category,
) {}
}
2. php artisan typescript:transform generates TypeScript types from those Data classes:
// Auto-generated — do not edit
export type PostData = {
title: string;
slug: string;
excerpt: string | null;
published_at: string; // Carbon → ISO string
category: CategoryData;
};
3. Inertia ships them as props to typed React components:
import type { PostData } from '@/types/generated';
export default function PostShow({ post }: { post: PostData }) {
return <article><h1>{post.title}</h1></article>;
}
Change a field in PHP → re-run typescript:transform → the React side has the new type → TypeScript fails the build if any component still references the old shape. The contract between server and client is enforced by the compiler, not by hope.
This is what most headless CMS + Next.js setups bolt on with graphql-codegen or hand-written types. In the monolithic stack, it's part of the workflow.
For a deeper look at why type safety matters as a CMS feature (not just a nice-to-have), see what makes a CMS developer-friendly.
The Rendering Pipeline: Public Site vs Admin
A monolithic modern CMS has two surfaces — the public site that visitors see, and the admin that editors use. The Laravel + React + Inertia stack handles them differently, and that split matters.
Public site (the visitor-facing pages, blog, marketing) renders server-side via Blade:
- Laravel routes hit a controller
- The controller fetches data and returns a Blade view
- Blade renders HTML on the server
- The browser gets full HTML on first byte — fast LCP, SEO-friendly, no React hydration cost on the public site
Admin (the editor and developer-facing pages — /admin/posts, /admin/users, settings) renders via Inertia + React:
- Laravel routes hit a controller
- The controller returns
Inertia::render('Admin/Posts/Index', [...]) - The browser gets the React app shell + props as JSON
- React renders the admin UI client-side — interactive, stateful, SPA-feeling
Why split it this way? Because the public site needs SEO, fast LCP, and works without JavaScript. The admin needs interactivity, form-heavy UX, and rich client-state (filters, modals, inline editing). React is the right tool for the admin. Server-rendered Blade is the right tool for the public site.
This split is invisible to editors — they edit content in the React admin, and the public site renders it via Blade templates that read the same database. It's also invisible to visitors — they see fast HTML pages and never load the React admin code.
For the React admin specifically, our 51 shadcn/ui components in a real production admin post breaks down what we built; for the broader stack story, Laravel + React + shadcn/ui: the modern CMS stack covers the React admin in depth.
When the Monolithic Modern CMS Stack Is Wrong
Honest assessment: this stack is the wrong pick for several real cases. Skip it if any of these apply:
1. You need a public headless API today.
If your project requires a separate Next.js, Vue, mobile, or kiosk frontend reading content from the CMS, the monolithic stack doesn't ship that today. UnfoldCMS specifically has a public REST + GraphQL API on the roadmap but not in production. Other Laravel+Inertia CMSes are similar — Inertia is the integration point, not REST. If you need a public API now, pick a headless CMS instead — see best CMS for React developers in 2026 for the headless options.
2. Your team isn't on Laravel.
The stack assumes Laravel comfort. If your team is Node + TypeScript, the language switch costs more than it saves. For TypeScript-only teams, Payload v3 (Next.js + in-process Local API) is the same shape with a different language. See our UnfoldCMS vs Payload comparison for the head-to-head.
3. Multiple frontend surfaces share the same content.
A site + a mobile app + a kiosk all reading the same content is a headless story. The monolithic stack assumes one consumer (the same Laravel app's frontend). If you have multiple, you'll either need to add a public API anyway (defeating the simplicity) or maintain separate content stores (defeating the "one source of truth" goal).
4. You want to choose a different frontend framework later.
Monolithic locks you into the runtime. If you'd ever want to move from React to Vue, or rebuild the frontend in a different stack, the monolith makes that a bigger lift than headless does. For projects with high frontend volatility, headless is more flexible.
5. You have a large existing investment in headless tooling.
If your team already runs a Next.js + Sanity stack at scale and it's working, the case to switch is much weaker than if you're greenfield. Use what works.
The honest summary: monolithic Laravel + React + Inertia wins for single-frontend, Laravel-comfortable, small-to-medium-team projects. It loses for multi-frontend, JS-only-team, large-scale-content-operation projects.
How This Compares to Payload v3 (The Closest Competitor)
The closest competitor in shape to Laravel + React + Inertia is Payload v3 on Next.js. Both are monolithic, both ship the admin as a customizable React app, both have type safety end-to-end, both deploy as a single artifact.
The honest differences:
| Dimension | Laravel + React + Inertia | Payload v3 + Next.js |
|---|---|---|
| Backend language | PHP 8.3 (Laravel) | TypeScript (Next.js) |
| Frontend language | TypeScript | TypeScript |
| Public-site rendering | Blade (server-rendered HTML) | Next.js (RSC + ISR) |
| Admin runtime | React 19 + Inertia | React 19 + Next.js |
| Database | MySQL/Postgres (Eloquent) | MongoDB or Postgres (Drizzle) |
| API integration | Inertia (no REST/GraphQL) | Local API (in-process) + auto-generated REST + GraphQL |
| Public headless API | On roadmap | Available out of the box |
| Hosting model | Linux + PHP-FPM + MySQL | Node.js + Postgres |
| TypeScript-only team fit | No (PHP backend required) | Yes |
Pick Laravel + Inertia if: your team is PHP-comfortable, you value the Eloquent ORM and Laravel's batteries-included approach (queues, jobs, scheduling, mail), and you're OK with Inertia as the integration point.
Pick Payload + Next.js if: your team is TypeScript-only, you want a public REST/GraphQL API today, and you're already building on Next.js.
Both are legitimate. The monolithic modern CMS category isn't WordPress vs Strapi — it's two camps of the same architectural pattern, in different languages. See UnfoldCMS vs Payload for the direct comparison if you're choosing between them, or the best CMS for Next.js page for the broader Next.js angle.
What to Do About It
If you're picking a CMS architecture in 2026, here's the order of operations:
- Answer the architecture question first. Do you need separate frontend services consuming a CMS API? If yes → headless. If no → monolithic. Most teams default to headless when they don't actually need it.
- Match the language. Laravel-comfortable teams → Laravel + Inertia. TypeScript-only teams → Payload + Next.js. Don't fight your language.
- Test the integration path live. Spin up a real install of whichever stack matches. Build a custom field, an admin page, a public route. The 2 hours of setup tells you more than reading docs for a week.
- Read the related posts — Laravel + React + shadcn/ui: the modern CMS stack covers our React admin in depth, the CMS built on shadcn/ui covers the design system story, and why your shadcn admin template should be a full CMS covers the bridge from "admin starter" to "real CMS."
- Run the TCO math — fewer services + simpler ops usually beats best-of-breed services + integration cost over a 3-year horizon. The exception is when you genuinely need the extra flexibility.
If your stack is Laravel + React + shadcn/ui and the monolithic shape fits, UnfoldCMS is built for that exact configuration — Laravel 12 + React 19 + Inertia 2 + shadcn/ui (51 components) + Tailwind v4. See pricing, book a demo, or explore the compare hub to see how this stack stacks up against headless options. We're honest about what we are: a Laravel+React monolithic CMS, not a Next.js headless backend.
FAQ
What is Inertia.js and how does it work with Laravel?
Inertia is a glue layer that lets server-side frameworks (Laravel, Rails, Django) render React, Vue, or Svelte components without writing an API. Your Laravel route returns Inertia::render('Component', [...props]) instead of HTML or JSON. The browser renders the React component with the props the server provided. Subsequent navigations fetch new props from the same routes, swapping components client-side. The result: SPA-feeling UX without an API surface to design or maintain.
Can I use the Laravel + React + Inertia stack as a headless CMS?
Not directly today. Inertia is the integration point — it ships React components with server-fetched props, not a public REST/GraphQL API. To use a Laravel + Inertia CMS as a headless backend, you'd write your own API routes (Laravel makes this trivial — Route::apiResource('posts', PostApiController::class)). UnfoldCMS specifically has a public REST + GraphQL API on the roadmap, not yet shipped. For headless-first projects today, pick a headless CMS instead.
Why use Laravel + React instead of just Next.js?
Laravel handles a lot of CMS-shaped work better than raw Next.js: queues for background jobs, scheduled tasks, Eloquent for complex queries, FormRequest validation, mail, broadcasting, file storage with Spatie Media Library. Next.js has equivalents but they're third-party packages or manual builds. For CMS-shaped projects (lots of CRUD, lots of background work, mature ORM needs), Laravel + React + Inertia ships faster than Next.js alone. For pure frontend-shaped projects (heavy on streaming, RSC, edge), Next.js wins.
What's the difference between this stack and Payload v3?
Both are monolithic modern CMSes with React admins, type safety, and single-deployable shapes. The differences are language and runtime: Laravel + Inertia is PHP backend + React frontend (separate languages, glued by Inertia); Payload v3 is TypeScript end-to-end on Next.js (one language, in-process API). For PHP-comfortable teams, Laravel wins on language familiarity and the Eloquent ORM. For TypeScript-only teams, Payload wins on language consistency and the Local API performance.
Is the Laravel + React + Inertia stack good for SEO?
Yes — when used correctly. The public site renders server-side via Blade (full HTML on first byte, fast LCP, no JavaScript required). The React admin doesn't need SEO. This split gives you the SEO of a server-rendered site and the UX of a SPA admin. The mistake is rendering the public site via Inertia + React — that puts hydration cost on the SEO-critical pages. Don't do that. Server-rendered Blade for the public site, Inertia + React for the admin.
How do I add a custom feature to a Laravel + React + Inertia CMS?
The pattern: add a route in routes/web.php, create a controller, return Inertia::render('Feature/Page', [...props]), write the React component in resources/js/pages/. For data, add an Eloquent model, write a Spatie Data class for type safety, run php artisan typescript:transform to generate the TypeScript type. The whole flow is server-side first (route + controller + model), client-side second (React component consuming the typed props). Customizing UnfoldCMS specifically follows the same pattern — every admin page is a React component fed by a controller.
Sources & Methodology
This post draws on:
- First-hand operational experience — UnfoldCMS runs on this exact stack (Laravel 12 + React 19 + Inertia 2 + shadcn/ui + Tailwind v4) and is what unfoldcms.com itself is built on
- Inertia.js official docs — inertiajs.com, checked May 2026
- Spatie Laravel Data docs — spatie.be/docs/laravel-data, for the typed-data + TypeScript transform pipeline
- Payload v3 architecture documentation — payloadcms.com/docs, for the Next.js + Local API comparison
- Production metrics — our own UnfoldCMS deployment serves the marketing site, blog, docs, and admin from one Laravel app on a single Hetzner CX22 VPS
Disclosure: we built UnfoldCMS on this stack and use it daily. The opinions are ours; the trade-offs section reflects real limitations (only a small read-only public JSON API for posts today; full headless mode is on the roadmap; Laravel-only stack) that matter for some teams. The monolithic-vs-headless framing isn't a marketing pitch — it's the architectural decision we'd run with any team picking a CMS in 2026, whether or not they end up on UnfoldCMS.
The Laravel + React + Inertia pattern existed before us — Statamic, Filament, and many bespoke Laravel apps use it. Our contribution is shipping it as a CMS product with shadcn/ui as the design language. The stack itself is well-trodden ground; this post is the case for picking it deliberately, not by default.
Free & Open Source
Own your CMS. No subscriptions.
Unfold CMS is free to download and self-host. Built on Laravel + React, full source code included.
Share this post: