NextJs Update from 13 to 15

Guide: Updating storefront-starter-kit from Next.js 13 to Next.js 15

This guide outlines the steps to upgrade storefront-starter-kit to Next.js 15, including updating dependencies, migrating routing logic, and aligning environment variable handling to match Next.js's built-in mechanisms.


1. Update Dependencies

Begin by upgrading your dependencies to ensure compatibility with Next.js 15:

npm i next@^15 react@^18 react-dom@^18 eslint-config-next@^15

2. Aligning Environment Variables

Previous Approach: dotenv-webpack

In older versions, your project might use dotenv-webpack to read .env files and make environment variables accessible in both server and client-side code. For example:

webpack: (config) => {
  config.plugins = config.plugins || [];

  config.plugins = [
    ...config.plugins,
    // Read the .env file
    new Dotenv({
      path: path.join(__dirname, '.env'),
      systemvars: true,
    }),
  ];

  return config;
},

Why Change?

  1. Conflicts in Next.js 15:
    The dotenv-webpack plugin conflicts with Next.js 15's middleware system, causing issues such as:

    • Middleware not functioning correctly.
    • Unexpected behavior during runtime.
  2. Native Next.js Support:
    Next.js now provides a robust way to handle environment variables without external plugins.

New Approach: Use Next.js .env Conventions

  1. Remove the dotenv-webpack plugin configuration from next.config.js.

  2. Rename your .env variables to align with Next.js conventions:

    • Public Variables: Prefix with NEXT_PUBLIC_ to make them accessible in client-side code.
    • Private Variables: Do not prefix with NEXT_PUBLIC_ to restrict them to server-side code.

Example .env Update:

Before:

API_URL=https://api.example.com
SECRET_KEY=my-secret-key

After:

NEXT_PUBLIC_API_URL=https://api.example.com
SECRET_KEY=my-secret-key

Important Notes:

  • Restart the development server after modifying .env.
  • Public variables (NEXT_PUBLIC_*) will automatically be accessible on both the client and server.
  • Private variables will remain server-side only.

3. Standard Routing: Replace app.render with rewrites()

In your server/index.js, you might find routing logic like this:

server.get('/pali', (req, res) => {
  app.render(req, res, '/library/entry', req.query);
});

Action: Use rewrites() Instead

  1. Remove the route from server/index.js.
  2. Add a rewrite rule in next.config.js:
module.exports = {
  async rewrites() {
    return [
      {
        source: '/pali',
        destination: '/library/entry',
      },
    ];
  },
};

4. Wildcard Dynamic Routing

For dynamic routing where all paths redirect to a specific page, replace this logic in server/index.js:

server.get(/^(.*)$/, (req, res) => {
  app.render(req, res, '/frontend/entry', {
    seoUrl: req.params[0],
    ...req.query,
  });
});

Step 1: Add Middleware

Create a middleware.js file in the root of your project:

import { NextRequest, NextResponse } from 'next/server';

export function middleware(req) {
  const pathname = req.nextUrl.pathname;

  // Match all paths using a wildcard pattern
  const pattern = /^(.*)$/;

  if (pattern.test(pathname)) {
    const url = req.nextUrl.clone();

    // Set `seoUrl` parameter based on the pathname
    const seoUrl = url.pathname;
    url.searchParams.set('seoUrl', seoUrl);

    // Preserve existing query parameters
    req.nextUrl.searchParams.forEach((value, key) => {
      url.searchParams.set(key, value);
    });

    // Rewrite the request to /frontend/entry
    return NextResponse.rewrite(url);
  }

  return NextResponse.next();
}

Step 2: Add a Wildcard Rewrite in next.config.js

Update the rewrites() function to include a wildcard rule:

module.exports = {
  async rewrites() {
    return [
      {
        source: '/pali', // Example standard route
        destination: '/library/entry',
      },
      {
        source: '/:path*', // Wildcard for all other paths
        destination: '/frontend/entry',
      },
    ];
  },
};

5. Remove Legacy Code

Delete Routing in server/index.js

Remove any app.render logic, including both standard routes and wildcard handlers:

server.get('/pali', (req, res) => {
  app.render(req, res, '/library/entry', req.query);
});

server.get(/^(.*)$/, (req, res) => {
  app.render(req, res, '/frontend/entry', {
    seoUrl: req.params[0],
    ...req.query,
  });
});

6. Test Your Application

  1. Restart the Development Server
    Start the app with:

    npm run dev
    
  2. Verify Environment Variables
    Test public and private variables:

    • Log process.env.NEXT_PUBLIC_API_URL in client-side code.
    • Log process.env.SECRET_KEY in server-side code.
  3. Verify Routing

    • Test /pali to ensure it rewrites to /library/entry.
    • Test wildcard routes like /example to confirm they rewrite to /frontend/entry with seoUrl and query parameters.

Why Migrate?

Environment Variables

  • Conflict Resolution: Removing dotenv-webpack resolves conflicts that break Middleware in Next.js 15.
  • Native Support: Next.js provides first-class support for .env handling without additional configuration.

Routing

  • Deprecation of app.render: Next.js 15 fully adopts the App Router, making custom server routing obsolete.
  • Performance: Middleware operates at the edge, reducing latency for dynamic routes.

By following this guide, your storefront-starter-kit will be fully compatible with Next.js 15, adhering to its best practices for environment variables, routing, and middleware.