$ ls ./menu

© 2025 ESSA MAMDANI

cd ../blog
5 min read
Web Development

Beyond the Monolith: A Definitive Guide to Migrating Laravel to Next.js

> Transition your Laravel application into a high-performance, modern React stack. This guide explores the architectural shifts, authentication strategies, and technical nuances of migrating from PHP to the Next.js ecosystem.

Audio version coming soon
Beyond the Monolith: A Definitive Guide to Migrating Laravel to Next.js
Verified by Essa Mamdani

Let’s be honest: Laravel is a masterpiece of engineering. Taylor Otwell and the community have built an ecosystem that is arguably the most productive in the world for traditional MVC applications. But the web is changing. Users expect "app-like" fluidity, and developers crave the component-based architecture and massive ecosystem of React.

If you’ve found yourself hitting the limits of Blade templates, struggling with complex Livewire states, or simply wanting to leverage the V8 engine’s raw speed and Next.js’s superior SEO capabilities through Server Components, you’re in the right place.

I’ve overseen several of these migrations. It’s not just a "rewrite"—it’s a strategic re-architecting. Here is how you do it right.


1. The Architectural Shift: From Monolith to Headless

The biggest hurdle isn't the syntax; it's the mental model. Laravel is a "batteries-included" monolith where the backend and frontend are tightly coupled. Next.js thrives in a "Headless" or "Decoupled" environment.

The Strangler Fig Pattern

Don't try a "Big Bang" migration where you stop all feature development for six months. Use the Strangler Fig Pattern. Start by putting Next.js in front of your Laravel app. Route specific new features or high-traffic pages (like your marketing site or blog) to Next.js, while the legacy Laravel app handles the complex CRUD operations behind the scenes.

Pro Tip: Use a reverse proxy like Nginx or Cloudflare Workers to route traffic based on the URL path. /api/* goes to Laravel, while /* goes to Next.js.


2. Phase One: Transforming Laravel into a Robust API

Your Laravel application must stop serving HTML and start serving JSON. Fortunately, Laravel makes this incredibly easy with API Resources.

The Laravel Side (The Data Provider)

Instead of returning a view, you’ll return a resource collection.

php
1// app/Http/Controllers/PostController.php
2public function index()
3{
4    $posts = Post::latest()->paginate(10);
5    return PostResource::collection($posts);
6}

The Next.js Side (The Data Consumer)

In the modern Next.js App Router, we fetch this data directly in a Server Component. This keeps your API keys secure and reduces client-side JavaScript.

tsx
1// app/blog/page.tsx
2async function getPosts() {
3  const res = await fetch('https://api.yourdomain.com/v1/posts', {
4    next: { revalidate: 3600 } // Cache for one hour
5  });
6  
7  if (!res.ok) throw new Error('Failed to fetch posts');
8  return res.json();
9}
10
11export default async function BlogPage() {
12  const { data: posts } = await getPosts();
13
14  return (
15    <main>
16      <h1>Latest Insights</h1>
17      {posts.map((post: any) => (
18        <article key={post.id}>
19          <h2>{post.title}</h2>
20        </article>
21      ))}
22    </main>
23  );
24}

3. The Authentication Bridge: Sanctum vs. NextAuth

This is where most migrations get stuck. Laravel uses session-based cookies by default, while many Next.js tutorials push for JWTs (JSON Web Tokens).

My recommendation: If you are keeping Laravel as your backend, stick with Laravel Sanctum. It provides a robust, cookie-based authentication system that works perfectly with Next.js, provided you handle the CORS and CSRF configurations correctly.

The Secret Sauce: CSRF Protection

Next.js must "check in" with Laravel to get a CSRF token before attempting a login.

  1. Next.js calls GET /sanctum/csrf-cookie.
  2. Laravel sets a cookie in the browser.
  3. Next.js sends the login request with the X-XSRF-TOKEN header.

Pro Tip: Use the next-auth library but configure a custom CredentialsProvider. This allows you to manage the session in Next.js while the "source of truth" for the user remains in the Laravel database.


4. Reimagining the Frontend: From Blade to TSX

Blade is elegant, but React components are powerful. During migration, you’ll move from @extends and @include to a component-based architecture.

Logic Placement

In Laravel, you likely have a lot of logic in your Blade files or View Composers. In Next.js, that logic moves:

  • Data Fetching: Moves to Server Components (page.tsx).
  • State Management: Moves to useState or useReducer in Client Components ('use client').
  • SEO: Moves from @yield('meta') to the generateMetadata function.

Example: Metadata Migration

Laravel (Blade):

html
1@section('title', $post->title)

Next.js (App Router):

tsx
1export async function generateMetadata({ params }) {
2  const post = await getPost(params.slug);
3  return {
4    title: post.title,
5    description: post.excerpt,
6  };
7}

5. Handling State: Moving Beyond Livewire

If you are coming from Livewire, the transition to React state might feel verbose. Livewire hides the "request/response" cycle. In Next.js, you are explicitly managing the client-side state.

For complex forms, I highly recommend React Hook Form combined with Zod for validation. This mirrors Laravel's Request Validation logic but moves the immediate feedback to the client's browser.

Pro Tip: Use TanStack Query (React Query) for your client-side data fetching. It handles caching, revalidation, and "optimistic updates" far more gracefully than manual useEffect calls. It brings that "magic" feeling back that you might miss from Livewire.


6. Deployment and Environment

Laravel belongs on a server (or a specialized host like Laravel Forge). Next.js belongs on a specialized edge platform like Vercel.

  • The Backend: Keep your Laravel API on its current infrastructure. Optimize it for performance by using php artisan octance to keep the application in memory.
  • The Frontend: Deploy Next.js to Vercel. This gives you global distribution, instant cache invalidation, and seamless CI/CD.

Handling Environment Variables

Ensure your NEXT_PUBLIC_API_URL is set correctly. Remember, variables prefixed with NEXT_PUBLIC_ are exposed to the browser. Keep your Laravel APP_KEY and database credentials strictly on the Laravel server.


Final Thoughts: Is it Worth It?

Migration is a high-effort endeavor. If your Laravel app is a simple internal CRUD tool, stay on Laravel. The productivity of the TALL stack (Tailwind, Alpine, Laravel, Livewire) is hard to beat for internal tools.

However, if you are building a high-traffic consumer product, a SaaS with a complex UI, or a platform where SEO and performance are the primary competitive advantages, migrating to Next.js is the right move. You gain access to the world’s largest UI ecosystem, the speed of the V8 engine, and a developer experience that is currently the industry gold standard.

Laravel is your engine; Next.js is your cockpit. Together, they are unstoppable.

Happy coding.