DEV Community

Alfredo Baldoceda
Alfredo Baldoceda

Posted on

Migrating from Next.js Pages Router to App Router

Summary

Migrating from Next.js Pages router to Next.js App router provides several benefits and introduces new concepts for organizing your Next.js project. In this guide, we will walk you through the process of migrating to the App router, explaining important concepts and highlighting key steps along the way. We will also explore the integration of Tailwind CSS into the project.


Introduction

Next.js is a popular React framework for building server-side rendered and static websites. With the introduction of the App router, Next.js offers a more structured approach to organizing your application's routes and components. This migration guide will help you understand the steps involved in adopting the Next.js App router and using it alongside Tailwind CSS.


Prerequisites

Before we begin, make sure you have the following requirements in place:

  • Node.js 16.8+ installed on your system.
  • A basic understanding of Next.js and Tailwind CSS.

Step 1: Installing Required Packages

To get started, navigate to your Next.js project directory and install the latest required packages for Next.js and Tailwind CSS. Open your terminal and run the following command:

npm install next@latest react@latest react-dom@latest tailwindcss@latest
Enter fullscreen mode Exit fullscreen mode

This command will update your project with the latest versions of Next.js, React, React DOM, and Tailwind CSS.


Step 2: Updating the Next.js and Tailwind Configuration

Next, we need to update the Next.js configuration to enable the App directory and configure Tailwind CSS accordingly. Locate the next.config.js file in your project and add the following lines:

module.exports = {
  experimental: {
    appDir: true,
  },
  // Other configuration options...
};
Enter fullscreen mode Exit fullscreen mode

Enabling the appDir option allows Next.js to recognize the App directory as the main directory for your application's routes.

Now, open the tailwind.config.js file and update the content property as follows:

module.exports = {
  // Other configuration options...
  content: [
    "./pages/**/*.{js,ts,jsx,tsx}",
    "./app/**/*.{js,ts,jsx,tsx}",
    "./components/**/*.{js,ts,jsx,tsx}",
  ],
};
Enter fullscreen mode Exit fullscreen mode

This change ensures that Tailwind CSS scans the App directory for CSS utility classes.


Step 3: Creating First App Page Component and Root Layout

To take full advantage of the App router, we need to reorganize our directory structure and create the initial App page component and root layout.

  1. Create a new directory called app in your project's root directory. This directory will hold your App-specific components and files.

  2. To avoid conflicts with new App router files, rename the existing pages directory to old-pages or choose a different name that is distinct from the default pages directory.

  3. Edit the page.tsx file inside the app directory with a simple React component representing your homepage:

   import React from 'react';

   export default function HomePage() {
     return <div>This is my new App Route Home Page</div>;
   }
Enter fullscreen mode Exit fullscreen mode
  1. Start your Next.js development server using the npm run dev command. This will generate the

layout.tsx file automatically for you.

   $ npm run dev
   ...
   wait - compiling /page (client and server) ...
   Your page app/page.tsx did not have a root layout. We created app/layout.tsx for you.
Enter fullscreen mode Exit fullscreen mode

After the server starts, your project structure should look like this:

   - app
     - index.tsx
     - layout.tsx
Enter fullscreen mode Exit fullscreen mode
  1. Since we now have a root layout file, we can add our Tailwind CSS styles to it. Open the layout.tsx file and import the globals.css file:
   import '../styles/globals.css';
Enter fullscreen mode Exit fullscreen mode

Step 4: Naming Convention

The App router follows a naming convention for its files. The files should be named page.tsx, layout.tsx, loading.tsx, etc., depending on their purpose. This convention helps Next.js identify and associate the components correctly with their routes.


Step 5: Understanding Layouts

The layout.tsx file in the App directory serves as a global layout for all child views. This means that any components placed in the layout file will be shared across multiple pages unless you create a new layout specifically for a particular page or group of pages. The layout file is an excellent place to define global settings, such as importing global libraries or configuring services like AWS Amplify.

For example, you can configure AWS Amplify in the layout.tsx file as follows:

import awsconfig from '../src/aws-exports';
import { Amplify, AuthModeStrategyType } from 'aws-amplify';

Amplify.configure({
  ...awsconfig,
  ssr: true,
  DataStore: {
    authModeStrategyType: AuthModeStrategyType.MULTI_AUTH,
  },
});

interface RootLayoutProps {
  children: React.ReactNode;
}

export default async function RootLayout({ children }: RootLayoutProps) {
  return (
    <html lang="en">
      <body>
        {children}
      </body>
    </html>
  );
}
Enter fullscreen mode Exit fullscreen mode

The layout component is responsible for rendering the shared structure, such as the HTML and body tags, while the children prop represents the content specific to each page.


Step 6: Using the "use client" Directive

By default, all code within the App router runs on the server. To execute code on the client-side, such as React hooks, you need to use the "use client" directive at the beginning of your document.

For example, in app/page.tsx, you can use the "use client" directive and implement a simple counter:

"use client"

import { useState } from "react";

export default async function Page() {
  const [counter, setCounter] = useState(0);

  return (
    <div>
      <p>Counter: {counter}</p>
      <button onClick={() => setCounter(prev => prev + 1)}>+1</button>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

The "use client" directive indicates that this code should run on the client-side, allowing the use of client-specific features like React hooks.


Step 7: Sending Components to the Client

The App router provides a convenient way to send only the necessary components to the client. This approach improves performance by reducing the amount of code sent to the browser.

For example, you can fetch data on the server and render a specific component on the client:

// All this code will be executed on the server

import { withSSRContext } from 'aws-amplify';
import { serialize } from 'next-mdx-remote/serialize';
import { Posts } from '../../../src/models';
import Post from '../../../components/Post';

async function fetchPost(slug) {
  const { DataStore } = withSSRContext();
  const post = await DataStore.query(Posts, slug);
  const mdxSource = await serialize(post.content);

  return {
    date: post.createdAt,
    title: post.title,
    content: mdxSource,
    slug,
  };
}

export default async function SlugPage({ params }) {
  const { slug } = params;
  const post = await fetchPost(slug);

  // The <Post/> component will be rendered on the client-side, as we use "use client" within it.
  return <Post {...post} />;
}
Enter fullscreen mode Exit fullscreen mode

Another good example, it is our Root Layout Component.

The Root Layout is a server component that calls the NavBar and AuthBtn components.

AuthBtn Component use the useAuthenticator hook which can only work on client components.

We call the AuthBtn component from our NavBar component. Both are client components.

AuthBtn component:

"use client";

import Link from "next/link";
import { useAuthenticator } from "@aws-amplify/ui-react";
import { Auth } from "aws-amplify";

export default function AuthBtn({ setter }) {
  const { authStatus } = useAuthenticator((context) => [context.authStatus]);

  return (
    <>
      {authStatus && authStatus !== "authenticated" ? (
        <SignInButton setter={setter} />
      ) : (
        <SignOutButton setter={setter} />
      )}
    </>
  );
}

Enter fullscreen mode Exit fullscreen mode

This approach ensures that only the necessary components are sent to the client, reducing the initial load time and improving the overall performance of your application.

For more details information about client components go to https://nextjs.org/docs/getting-started/react-essentials#the-use-client-directive.


Step 8: Leveraging SEO Metadata

The App router provides a convenient way to handle metadata, such as page titles, descriptions, and keywords. You can export metadata from your views and layouts to ensure proper SEO optimization.

In your view components, export the metadata as follows:

export const metadata = {
  title: 'App Route',
  description: 'First metadata in app route',
  keywords: ['react', 'nextjs'],
};
Enter fullscreen mode Exit fullscreen mode

You can also define general metadata in your layout component. Next.js is intelligent enough to prioritize view-specific metadata over layout metadata.

export const metadata = {
  title: 'App Route',
  description: 'First metadata in app route',
  keywords: ['react', 'nextjs'],
};

export default function Layout({ children }) {
  return (


 <main>
      {children}
    </main>
  );
}
Enter fullscreen mode Exit fullscreen mode

By leveraging metadata, you can improve the search engine visibility and discoverability of your application's pages.


Our Website running NextJS App Router

See our current Website laveraging App Router in our github repo: https://github.com/albac/albac.dev

Look at our current structure to see how we organize your pages and components:


app/api/davinci:
route.ts

app/api/turbo:
route.ts

app/blog-edit:
[id]/

app/blog-edit/[id]:
page.tsx

app/chatgpt:
[model]/

app/chatgpt/[model]:
page.tsx*   layout.tsx

app/legal:
privacy-policy/   terms-conditions/

app/legal/privacy-policy:
page.tsx

app/legal/terms-conditions:
page.tsx

app/projects:
loading.tsx  page.tsx

app/signin:
page.tsx

app/storagemanager:
page.tsx*

app/profile:
loading.tsx        image-profile.tsx  page.tsx

Enter fullscreen mode Exit fullscreen mode

Conclusion

Migrating to the Next.js App router introduces a more structured approach to organizing your application's routes and components, making it easier to build and maintain complex applications. In this guide, we specifically looked at how you can migrate a blog application to the App router.

By migrating your blog application to the Next.js App router, you gain a more structured and organized approach to building your blog. The separation of concerns, client-side rendering capabilities, and SEO optimization features provided by the App router enhance the overall user experience and make your blog more efficient and discoverable.

We hope this guide has been helpful in understanding and implementing the Next.js App router in your blog application. For more detailed information and examples, refer to the official Next.js documentation on https://nextjs.org/docs/app.


References


Top comments (1)

Collapse
 
alexbit profile image
Alex Bit

awesome article. thanks for sharing. would love to get your opinion on what we do (
marketplace.visualstudio.com/items...
docs.intuita.io/docs/vs-code-exten...
notion.so/intuita/Nextjs-Pages-to-... ).

lemme know if you want me to show you a demo. linkedin.com/in/alexbit/