Skip to main content

Why We Rebuilt Our Entire Stack with Laravel + Next.js (And What Happened to Performance)

Leo Liebert
NR Studio
3 min read

Three years ago, we were running a classic Laravel monolith that served our main client’s platform. It worked. It was maintainable. It was also starting to crack under the weight of 200,000 monthly active users and a product team that wanted to ship features faster than our MPA (Multi-Page Application) architecture allowed.

This is the story of how we went headless, what we actually gained, and one decision we wish we’d made differently from the start.

Why We Moved Away From the Monolith

The monolith wasn’t failing. But it was accumulating friction:

  • Every front-end change required a full Laravel deploy cycle
  • The design team couldn’t iterate independently
  • Mobile (a Flutter app) was duplicating API logic in places it shouldn’t have to
  • Page load times were acceptable but not excellent — averaging 2.1s on mobile

The breaking point was when the product team requested a real-time dashboard feature. WebSockets in a Laravel + Blade architecture is doable, but it adds complexity that a proper API-first stack handles naturally.

The Architecture Decision

After evaluating the options (Nuxt.js, SvelteKit, Remix, Next.js), we chose Next.js 14 for the front-end and kept Laravel as the API backend — now exposing a clean REST + GraphQL surface.

The reasons:

  • Next.js App Router gave us granular control over static vs. dynamic rendering per route
  • React Server Components reduced our client-side JavaScript bundle by 40%
  • Laravel’s ecosystem for queues, jobs, and notifications is still best-in-class
  • Our team already knew both stacks deeply

What Actually Improved

Six months after the migration, the numbers were clear:

  • Largest Contentful Paint (LCP): 2.1s → 0.9s
  • Time to First Byte (TTFB): 420ms → 85ms (ISR + edge caching)
  • Total Blocking Time: 380ms → 45ms
  • Core Web Vitals score: 67 → 98

The front-end team’s deploy cycle went from 25 minutes (full stack) to 4 minutes (Next.js preview deployments). That alone changed how fast we could iterate on UI.

The One Thing We’d Do Differently

We introduced GraphQL too early. We built a full GraphQL schema on day one because it felt like the right architecture. In practice, the first 6 months of the new front-end only needed 8 REST endpoints. The GraphQL infrastructure added cognitive overhead for the team without delivering meaningful benefits at that stage.

Our recommendation: start with REST, design your endpoints thoughtfully, and introduce GraphQL when you genuinely need the query flexibility — typically when you have more than 3 consuming clients with different data shape requirements.

Lessons Learned

Headless architecture is not magic. It is a trade-off. You trade deployment simplicity for performance flexibility. You trade a single codebase for faster team parallelism. For products at scale with distinct front-end and back-end teams, it’s usually worth it. For a solo developer building an MVP, it’s almost certainly premature optimization.

Choose your architecture based on where your bottlenecks actually are, not where you imagine they might be in two years.

Leave a Comment

Your email address will not be published. Required fields are marked *