How to Migrate nextjs v12 to v13 app Folder

New existing changes coming in to nextjs! here's an helpful general purpose guideline to how to migrate your nextjs v12 implementation to v13.

Intro

Next.js, a popular framework for building server-rendered React applications, has released version 13 with an experimental new design that introduces an "app" folder. This article provides a step-by-step migration guideline to help you migrate your code from Next.js 12 to Next.js 13, with code examples for both versions side by side. Follow the instructions below to ensure a smooth transition.

Step 1 - Configuration

Configuring next.config.js with "appDir" set to true To start the migration process, you need to configure your next.config.js file to support the new "app" folder feature. This can be done by setting the "appDir" option to true.

By setting the "appDir" option to true in Next.js 12, you are informing Next.js to look for the "_app.js" and "_document.js" files inside the "app" folder instead of the default "pages" folder. This structure is meant to help and ease management of files and their purpose in our web application, however, that structure is not code-scalable or "framework" like structure. hence why nextjs is coming with the new "app" folder structure.

To enable the "app" folder feature in Next.js 13, update your next.config.js file as follows:

// Next.js 13
module.exports = {
  // Your configurations here
  experimental: {
    appDir: true,
  },
};

By setting the appDir option to true, you configure Next.js 13 to look for the "app" folder and a group of files that describe routing, error handling, page management, layout, templates, and more. These files will help you better organize your project, and we'll talk about them in the following steps.

Step 2 - Understanding the General Project Structure of the "app" Folder

The "app" folder is designed to improve the organization of your Next.js project by grouping essential files that handle routing, error handling, page management, layout, and templates together. In this section, we will discuss the general structure of the "app" folder and how URLs are mapped to folders and the "page.tsx" file.

The nextjs v12 (and still supported in v13 and beyond it's important to mention) example might look like this:

my-nextjs-app/
├─ src/
│  ├─ _app.tsx
│  ├─ _document.tsx
│  ├─ index.tsx
│  ├─ profile/
│  │  ├─ [pageid].tsx
│  ├─ blogPost/
│  │  ├─ create.tsx
│  ├─ comments/
│  │  ├─ showComment.tsx
│  ├─ pages/
│     ├─ api/
├─ public/
├─ styles/
└─ next.config.js

and v13 "app folder" approach might look like this:

my-nextjs-app/
├─ app/
│  ├─ layout.tsx // our main layout of our web app
│  ├─ template.tsx // optional, in case you have few repetative "structures"
│  ├─ page.tsx // "page.tsx" is a type of a file. see error.tsx and loading.tsx etc below
│  ├─ profile/
│  │  ├─ page.tsx
│  │  ├─ error.tsx
│  │  └─ loading.tsx
│  ├─ blogPost/
│  │  ├─ page.tsx
│  │  ├─ error.tsx
│  │  └─ loading.tsx
│  └─ comments/
│     ├─ page.tsx
│     ├─ error.tsx
│     └─ loading.tsx
├─ public/
├─ styles/
└─ next.config.js

In Next.js 12, the code is organized within a "src" folder, with top-level files such as app.tsx, document.tsx, and index.tsx. There is also an "api" folder inside "pages" for API routes. this approach is more "free" and less "framework" which explain's why vercal engineers decided to go toward more "strict" approach as seen below in v13.

In Next.js 13, the new "app" folder is introduced to organize your project structure more strictly. Inside the "app" folder, you'll find top-level files like layout.tsx, template.tsx, and page.tsx. in addition to those "type" like or "having-a-specific-purpose" file names you'll find things like "error.tsx" or "page.tsx" or "loading.tsx" each of those serve a specific purpose with the idea of reaching a more "framework" like experience.

Step 3: Understanding the Different File Names in the "app" Folder

In the new "app" folder structure introduced in Next.js 13, you will come across several file names that serve specific purposes within the framework. Let's discuss the roles of layout.tsx, template.tsx, page.tsx, loading.tsx, and error.tsx.

layout.tsx: The layout.tsx file is responsible for defining the overall structure of your application, such as header, footer, and sidebars. You can create a Layout component that wraps around your page components to maintain a consistent appearance across all pages. This makes it easier to manage the layout of your application in one central location. it's also recommended that layout will be one per project/web application. and other structure changes will occure in different templates.

export default function RootLayout({ /* nextjs 13 app folder approach */
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  )
}

template.tsx: The template.tsx file is used to define different structures of your web application for your pages. Templates are used to describe different parts of your web application. so for instance, maybe most of your sites have header, main cotent, sidebar and footer. but in a few pages you don't have sidebar and don't have footer. in this case, just create a new template!

page.tsx: The page.tsx file is the main component for a specific page in your application. Each subfolder within the "app" folder should contain a page.tsx file and its parent folder will describe the url itself. For example, the page.tsx file inside the "profile" folder would render the content for the "/profile" URL.

loading.tsx: The loading.tsx file is responsible for displaying a loading state while your page components are being fetched or rendered. This is particularly useful when you have components that rely on asynchronous data fetching, as it provides visual feedback to users and improves the overall user experience. this allows for better user experience and pre-planned communication with the user that some content is loading (e.g footer might have one, sidebar, main page content might have different loading etc).

error.tsx: The error.tsx file is used to define custom error components for specific pages or groups of pages in your application. This allows you to handle errors more effectively by displaying user-friendly error messages and providing alternative actions for users when something goes wrong. Each folder within the "app" folder can have its own error.tsx file, which would handle errors specific to that section of your application.

By using these different file names in the "app" folder, you can better organize and manage the various aspects of your Next.js application. This new structure promotes a clear separation of concerns, making it easier to maintain your project as it grows and evolves. and make sure that future development hold to same standard of UX.

Step 4: Implementing Basic SEO Meta Tags in Next.js

Search engine optimization (SEO) is crucial for improving the visibility of your website in search engines. One of the key aspects of SEO is using meta tags to provide information about your pages to search engines. In this section, we will discuss how to define basic SEO meta tags in a Next.js application.

In the current design of nextjs, To implement SEO meta tags in your Next.js application, you can use the built-in Head component from the next/head package. This component allows you to append elements to of your HTML document. Here's an example of how to add basic SEO meta tags to your page.tsx file:

import Head from 'next/head';

const MyPage = () => {
  return (
    <>
      <Head>
        <title>My Page Title</title>
        <meta name="description" content="A description of my page" />
        <meta name="keywords" content="keyword1, keyword2, keyword3" />
        <meta property="og:title" content="My Page Title" />
        <meta property="og:description" content="A description of my page" />
        <meta property="og:type" content="website" />
        <meta property="og:image" content="https://example.com/path/to/image.jpg" />
        <meta property="og:url" content="https://example.com/my-page" />
        <meta name="twitter:card" content="summary_large_image" />
        <meta name="twitter:title" content="My Page Title" />
        <meta name="twitter:description" content="A description of my page" />
        <meta name="twitter:image" content="https://example.com/path/to/image.jpg" />
      </Head>
      <main>
        {/* Your page content goes here */}
      </main>
    </>
  );
};

export default MyPage;

you can quickly see the problem in above design, each time you want to describe meta tags you'll need to re-create the entire page structure and than re-apply all the meta HTML tags. now let's see below new "app" design approach

// page.tsx
import styles from './page.module.css'

// v13 "app" folder approach
export const metadata = {
  title: 'Create Next App',
  description: 'Generated by create next app',
}

export default function Home() {
  return (
    <main className={styles.main}>
      Example
    </main>
  )
}

With the new approach, metadata can be partially change between heresy of each page.tsx file and folders. rather than copying and reapplying entire structure.

Step 5: Comparing Data Fetching Methods in the current and new "app" folder approach

Data fetching is a critical aspect of any modern web application, and Next.js offers various options to accommodate different use cases. In this section, we'll discuss the differences between data fetching methods in Next.js 12 and Next.js 13 "app" folder design.

Current Next.js 12

In Next.js 12 (and above, and will continuously be supported in future versions of nextjs), data fetching is commonly achieved using one of the following methods:

getStaticProps: This function is used to fetch data at build time and generate static HTML files. It's suitable for pages that don't need to be updated frequently or for content that doesn't change often. The fetched data is used as props for the page component.

getServerSideProps: This function fetches data on each request, making it ideal for pages with frequently changing or user-specific content. Similar to getStaticProps, the fetched data is passed as props to the page component.

// Next.js 12
export async function getStaticProps() {
  const response = await fetch('https://api.example.com/data');
  const data = await response.json();

  return {
    props: {
      data,
    },
    revalidate: 60, // Revalidate the data every 60 seconds
  };
}

Next.js 13 - "app" folder approach

In Next.js 13, the new "app" folder approach promotes the use of simple async functions for data fetching. You can use the native fetch function along with internal configurations to manage caching and other options. This method provides a more flexible way to fetch data while keeping your codebase clean and easy to maintain.

// Next.js 13 "app" folder approach
async function fetchHomepagePosts() { // create async functions that describe async https requst calls
  let res = await fetch(`${process.env.API_URL}/getPosts`, {
      method: "GET",
      headers: {
          "Content-Type": "application/json",
          'Accept': 'application/json'
      },
  });

  if(!res.ok) { // handle failing 
      throw new Error('Fail fetch');
  }

  return res.json();
}

export default async function Home() { // don't forget async
  const posts = await fetchHomepagePosts(); // can and should use Promise.all with multipile async function calls to improve performance 
  return (
    <main className={styles.main}>
      Example
    </main>
  )
}

Step 6: Comparing Navigation in Next.js 12 and Next.js 13

Navigation is a crucial aspect of any web application, and Next.js provides different approaches for managing navigation between pages.

Next.js 12:

In Next.js 12, you use the next/router package to manage navigation. The useRouter hook and the withRouter Higher-Order Component (HOC) provide access to the router instance, allowing you to navigate programmatically, access query parameters, and more.

Example:

// Next.js 12 (that will conitune to stay with us) and is still valid to use:
import { useRouter } from 'next/router';

const MyComponent = () => {
  const router = useRouter();

  const handleClick = () => {
    router.push('/new-page');
  };

  return (
    <div>
      {/* Your component content */}
      <button onClick={handleClick}>Go to New Page</button>
    </div>
  );
};

export default MyComponent;

Next.js 13:

In Next.js 13, the navigation approach has been refined with the introduction of the next/navigation package. This package provides a simpler and more intuitive API for navigation, replacing the router-based navigation with a new navigation system.

Example:

// Next.js 13 - "app" folder approach. 
import { useNavigation } from 'next/navigation'; // use navigation instead of router. there are few differences, see below list.

const MyComponent = () => {
  const navigation = useNavigation();

  const handleClick = () => {
    navigation.navigate('/new-page');
  };

  return (
    <div>
      {/* Your component content */}
      <button onClick={handleClick}>
    </div>
   )
}

few differences we can see:

  1. The new useRouter hook should be imported from next/navigation and not next/router.

  2. The pathname string has been removed and is replaced by usePathname()

  3. The query object has been removed and is replaced by useSearchParams() router.events is not currently supported

The above list was taken from the official nextjs documentation of router, read more here.

Summary

The new "app" folder approach looks promising, specially in the SEO, data fetching (avoiding getServerSideProps, getStaticProps), and the expected "framework" like file names like loading, error, page etc looks pretty promising design to come. bottom line, If your project works, then you can stay as is a little bit longer. but for new pages or for long-term planning, you can already convert to the new "app" folder structure (with the above configuration) and continue with step-by-step migration.