Updated my Next.js project to v13 Part1: Migrated app router
Tags: nextjs, react
Published: 2023-05-28 07:43:18

This is a series of my memo from when I migrated Next.js v13 into my project with the article [From Pages to App](https://nextjs.org/docs/pages/building-your-application/upgrading/app-router-migration#migrating-_documentjs-and-_appjs). I also updated other packages’ versions.

**Part1: Migrated app router from page router**

[Part2: Migrated router API](https://medium.com/@lada496/updated-my-next-js-project-to-v13-part2-hooks-for-app-router-38a82a8e5b0)

[Part3: Applied some other Next.js v13 updates](https://medium.com/@lada496/updated-my-next-js-project-to-v13-part2-link-use-client-3f5e17d12b8f)

[Optional: Updated NextAuth from v3 to v4](https://medium.com/@lada496/updated-nextauth-from-v3-to-v4-4210f2b994e3)

[Optional: Updated Redux from classical Redux to Redux Toolkit Query](https://medium.com/@lada496/optimistic-updates-with-rtk-query-4770698de6c1)

## _app.js and _document.js → app/layout.js

These are main key points:

* app directory supports nested routes and layout.

* page.js is for defining a specific route UI

* layout.js is for defining sharing UI among multiple routes

Suppose we have https:something and https:something/home, the folder structure of app directory will be:
```
    app - layout.js // This layout will apply to all routes inside app
          page.js
          home - layout.js // The specific layout for home page
                 page.js
```
```javascript
    // Before
    pages/_app.js
    import { Provider } from "react-redux";
    import { Provider as AuthProvider } from "next-auth/client";
    import { wrapper } from "../redux/store";
    import { useStore } from "react-redux";
    import { PersistGate } from "redux-persist/integration/react";
    import Head from "next/head";
    import Layout from "../components/layout/layout";
    import "semantic-ui-css/semantic.min.css";
    import "../styles/globals.css";
    
    function App({ Component, pageProps }) {
      const store = useStore((state) => state);
      return (
        <AuthProvider session={pageProps.session}>
          <Provider store={store}>
            <Head>
              <meta charSet="utf-8" />
              <meta name="viewport" content="width=device-width, initial-scale=1" />
              {/* <link
                rel="stylesheet"
                href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css"
                integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3"
                crossOrigin="anonymous"
              />
              <link rel="preconnect" href="https://fonts.googleapis.com" />
              <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin /> */}
              <link
                href="https://fonts.googleapis.com/css2?family=Italiana&family=Poiret+One&family=Spartan:wght@200;300;400;500;700&display=swap"
                rel="stylesheet"
              />
            </Head>
            {process.browser ? (
              <PersistGate
                persistor={store.__persistor}
                loading={<div>Loading</div>}
              >
                <Layout>
                  <Component {...pageProps} />
                </Layout>
              </PersistGate>
            ) : (
              <PersistGate persistor={store}>
                <Layout>
                  <Component {...pageProps} />
                </Layout>
              </PersistGate>
            )}
          </Provider>
        </AuthProvider>
      );
    }
    
    export default wrapper.withRedux(App);
```
```javascript
    // After
    import ReduxProvider from "../client/providers/redux-provider";
    
    import AuthProvider from "../client/providers/auth-provider";
    import Layout from "../components/layout/layout";
    
    import "semantic-ui-css/semantic.min.css";
    import "../styles/globals.css";
    
    function RootLayout({ children }) {
      return (
        <html lang="en">
          <head>
            <link
              href="https://fonts.googleapis.com/css2?family=Italiana&family=Poiret+One&family=Spartan:wght@200;300;400;500;700&display=swap"
              rel="stylesheet"
            />
          </head>
          <body>
            <AuthProvider>
              <ReduxProvider>
                <Layout>{children}</Layout>
              </ReduxProvider>
            </AuthProvider>
          </body>
        </html>
      );
    }
    
    export default RootLayout;
```
## **<Head> -> medadata**

* I don’t need to set charSet and viewport meta tags because these two are always added as the default ([Default Fislds](https://nextjs.org/docs/app/building-your-application/optimizing/metadata#default-fields)).

* It is still necessary to import <link ref=”stylesheet” /> at app/layout.js as this metadata type currently doesn’t have build-in support([Unsupported Metadata](https://nextjs.org/docs/app/api-reference/functions/generate-metadata#unsupported-metadata))

* metadata is only available for SSR
```javascript
    // Before
    import { useSelector } from "react-redux";
    import Head from "next/head";
    import { getSession } from "next-auth/client";
    
    import CheckoutList from "../../components/checkout-page/checkout-list";
    import Message from "../../components/ui/message";
    
    const ChackoutPage = () => {
      const cartItems = useSelector((state) => state.cart.cartItems);
      return (
        <>
          <Head>
            <title>Checkout</title>
            <meta name="description" content="Checkout Page" />
          </Head>
          <h1 className="h1">Checkout</h1>
          {(!cartItems || cartItems.length === 0) && (
            <Message text="No items added yet!" />
          )}
          {cartItems.length >= 1 && <CheckoutList list={cartItems} />}
        </>
      );
    };
```
```javascript
    // After
    // app/checkout/layout.js
    export const metadata = {
      title: "Checkout page",
    };
    export default function CheckoutLayout({ children }) {
      return <>{children}</>;
    } 
```
## Suspense and loading.js

loading.js and Suspense components from React provide you with a meaningful Loading UI.

Suspense provides better code optimization ([Streaming Server Rendering and Selective Hydration](https://nextjs.org/docs/app/building-your-application/routing/loading-ui-and-streaming#streaming-with-suspense)).

You can make use of [Instant Loading States](https://nextjs.org/docs/app/building-your-application/routing/loading-ui-and-streaming#instant-loading-states) when you put loading.js under the app router folder.

Here is an example.
```
    // folder structure
    app - layout.js // This layout will apply to all routes inside app
          page.js
          shop- [category]
            [productId] - layout.js
                          page.js
                          loading.js
```
```javascript
    // layout.js
    import { Suspense } from "react";
    import Loading from "./loading";
    export default function ProductLayout({ children }) {
      return <Suspense fallback={<Loading />}>{children}</Suspense>;
    }
```javascript
## references

* [https://nextjs.org/docs/app/api-reference/functions/generate-metadata](https://nextjs.org/docs/app/api-reference/functions/generate-metadata)

* [https://nextjs.org/docs/app/building-your-application/optimizing/metadata#seo](https://nextjs.org/docs/app/building-your-application/optimizing/metadata#seo)

* [https://nextjs.org/docs/app/building-your-application/data-fetching/fetching](https://nextjs.org/docs/app/building-your-application/data-fetching/fetching)

* [https://nextjs.org/docs/pages/building-your-application/upgrading/app-router-migration#step-4-migrating-pages](https://nextjs.org/docs/pages/building-your-application/upgrading/app-router-migration#step-4-migrating-pages)

* [https://nextjs.org/docs/app/building-your-application/routing/loading-ui-and-streaming](https://nextjs.org/docs/app/building-your-application/routing/loading-ui-and-streaming)

original article is [here](https://medium.com/@lada496/updated-my-next-js-project-to-v13-part1-migrated-app-router-27c3ce172bba)