The question we actually had to answer
We run a content-heavy site — blog posts, tool pages, category listings — on Laravel with Blade templates. Periodically someone proposes rewriting the front end in Next.js, usually after reading a case study about a company that migrated and saw big wins. We went through the exercise seriously: what would we actually gain, what would we give up, and does the answer change depending on which part of the site we're talking about. This isn't a "framework X is dead" post — both are good tools solving different problems, and the honest answer for us was to keep Blade for most of it, and we still don't regret that.
What SSR/SSG actually means for a site like this
Blade in Laravel is server-rendered on every request by default, with response caching layered in where it makes sense — a tool page or blog post that doesn't change per-visitor is easy to cache at the HTTP or view level without extra infrastructure. Next.js gives you a menu of options per route: static generation at build time, incremental static regeneration, or server-side rendering per request, plus client-side rendering for anything genuinely interactive after the initial load.
For a content site, that flexibility is real but it's also a decision you now have to make correctly for every route, and getting it wrong in either direction has consequences — over-cache a page with build-time SSG and your content update won't show up until the next deploy or ISR revalidation window; under-cache and you're paying server-rendering cost on every request for content that rarely changes. Blade's default (render fresh per request, add caching deliberately where a page's cost profile justifies it) is a less elegant model but a much simpler one to reason about for a small team, because there's one mental model instead of three.
Hosting complexity is the part people underweight
A Laravel app deploys to more or less any server that runs PHP — a single VPS, shared hosting in a pinch, or a managed platform. Nothing about serving Blade views requires anything beyond a normal PHP-FPM setup and a web server in front of it. Next.js, once you're using SSR or ISR rather than pure static export, effectively wants a Node runtime in production, and the ergonomics of that vary a lot depending on host: it's genuinely pleasant on Vercel specifically, and noticeably more work everywhere else, including self-hosted Node servers where you're now responsible for process management, memory limits, and Node version upgrades on top of everything you already manage for the rest of the stack.
For a small team already running Laravel apps and comfortable with that operational model, adding a second runtime and deployment pipeline just for the front end is a real, ongoing cost — not just a one-time migration cost — and it's the part that gets left out of "we switched to Next.js and it was great" case studies written by teams that were already deploying to Vercel for other reasons.
SEO control: closer than people assume, but not identical
Both stacks can produce fully-rendered HTML that search engines see immediately, which was the whole SEO argument for SSR in the first place — that part is a solved problem either way at this point, and neither stack has an inherent ranking advantage from rendering strategy alone. Where they differ is day-to-day control: with Blade, meta tags, structured data, canonical URLs, and sitemap generation are just server-side logic you already know how to write and test, with no separate data-fetching layer to keep in sync. With Next.js you get the same capability through its metadata API and generateStaticParams-style patterns, but it's another API surface to learn and another place a caching layer can accidentally serve stale meta tags to a crawler — a real failure mode we've seen on other teams' ISR-based sites, where a page's content updates but its cached <title> doesn't, because they're generated together but cached on different timers than expected.
Dev velocity for a small team, specifically
This is where the decision actually turned for us. Most of our tool pages — things like a PDF compressor or an image compressor — are fundamentally a form, a server-side processing step, and a result. That's a workflow Blade and a controller handle in very little code:
<!-- resources/views/tools/pdf-compressor.blade.php -->
@extends('layouts.app')
@section('content')
<h1>PDF Compressor</h1>
<form method="POST" action="{{ route('pdf-compressor.compress') }}" enctype="multipart/form-data">
@csrf
<input type="file" name="file" accept="application/pdf" required>
<button type="submit">Compress</button>
</form>
@endsection
Building the same tool in Next.js means deciding whether the compression runs in a route handler or a separate API, wiring a client component for the upload state, and duplicating some of the validation logic that would otherwise live once in a Laravel form request. None of that is hard, but it's more code and more decisions for a team of a handful of developers who are already fluent in PHP and don't get anything back for that extra surface area on a page with no real client-side interactivity to justify it.
Where a JS framework earns its complexity is genuinely interactive tools — live-updating previews, drag-and-drop editors, anything with meaningful client state that outlives a single form submission. We do reach for more JavaScript on those specific pages. We just don't default to it for every route on the site.
What a partial migration would actually look like
The proposal we actually evaluated seriously wasn't "rewrite everything" — it was "keep Blade for content and tools, add Next.js just for a couple of genuinely interactive pages." On paper that sounds like the best of both worlds. In practice it means running two separate deploy pipelines, two dependency ecosystems to keep patched, and a decision on every new page about which stack it belongs in, made by whoever happens to be building it that week rather than by a documented rule. We didn't rule this out permanently, but we decided it only earns its complexity once we have enough genuinely interactive pages to justify a second stack, not for one or two isolated cases where a bit more vanilla JavaScript inside an existing Blade page does the job just as well without a second runtime to operate.
The other real option we considered, and the one we're most likely to reach for if a page genuinely needs rich client interactivity, is mounting a scoped React or Vue component inside an otherwise normal Blade page rather than handing the entire page over to a separate framework. That gets you real client-side state where you actually need it — a live preview, a drag-and-drop reorder — without giving up server-rendered content, one deployment pipeline, or the SEO and caching model that works well for the rest of the site.
Image handling: one clear place Next.js pulls ahead
To be fair to the other side of this comparison, next/image's automatic responsive sizing, lazy loading, and format negotiation is genuinely more convenient out of the box than what Blade gives you by default, which is closer to "write the img tag and handle optimization yourself." We closed most of that gap manually — running uploads through our own image compressor before they reach production, and writing explicit width/height and loading="lazy" attributes by hand in the Blade component that renders tool card images — but it's manual work that next/image gives you close to automatically. If image-heavy pages were a bigger share of what we ship, this specific gap would weigh more heavily in the decision than it currently does for a mostly form-and-text content site.
The comparison, side by side
| Consideration | Laravel + Blade | Next.js |
|---|---|---|
| Default rendering | Server-rendered per request; caching added deliberately | Per-route choice of SSG, ISR, or SSR |
| Hosting | Any standard PHP host or VPS | Best on Node-friendly platforms; more setup elsewhere |
| SEO tags and sitemaps | Plain server-side logic, one mental model | Metadata API; extra care needed with cache timing |
| Best fit | Content pages, forms, server-processed tools | Highly interactive, stateful client experiences |
| Team fit | Small PHP-fluent teams shipping content fast | Teams already invested in the React/Node ecosystem |
| Ongoing ops | One runtime to manage | Two runtimes if the backend isn't already Node |
Where we landed
We kept Blade for the entire content and tool surface of the site and have no active plan to change that. The rendering-speed argument that usually drives these rewrites turned out, for us, to be solvable with the kind of template and asset-level work we cover in our Core Web Vitals writeup — most of what was actually slow wasn't a rendering-architecture problem at all. If we were starting a genuinely interactive product tomorrow — something closer to an editor or a dashboard than a content site — the calculation would likely flip, and we'd be having a very different conversation. The mistake we see other small teams make is picking a framework based on what's exciting to build with rather than what the specific pages on their specific site actually need.
A question worth asking before you migrate anything
- Which of your pages are genuinely interactive versus "form in, result out"? Be honest — most content sites have fewer of the former than they assume.
- Who's operating this in production in a year — the same team, with the same runtime skills, or someone new?
- Is your current bottleneck actually rendering architecture, or is it images, fonts, and layout shift that would follow you into any framework?
- Accessibility work is framework-agnostic — the common a11y mistakes we see show up in Blade templates and React components equally, so "we're rewriting the front end" is not itself an accessibility roadmap.
No comments yet.
Be the first visitor to add a thoughtful comment on this article.