DEV Community

Cover image for Updated my Next.js project to v13 Part1: Migrated app router
Yuko
Yuko

Posted on

Updated my Next.js project to v13 Part1: Migrated app router

This is a series of my memo from when I migrated Next.js v13 into my project with the article From Pages to App. I also updated other packages’ versions.

Part1: Migrated app router from page router

Part2: Migrated router API

Part3: Applied some other Next.js v13 updates

Optional: Updated NextAuth from v3 to v4

Optional: Updated Redux from classical Redux to Redux Toolkit Query

_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
Enter fullscreen mode Exit fullscreen mode
    // 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);
Enter fullscreen mode Exit fullscreen mode
    // 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;
Enter fullscreen mode Exit fullscreen mode

-> medadata

  • I don’t need to set charSet and viewport meta tags because these two are always added as the default (Default Fislds).

  • It is still necessary to import at app/layout.js as this metadata type currently doesn’t have build-in support(Unsupported Metadata)

  • metadata is only available for SSR

    // 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} />}
        </>
      );
    };
Enter fullscreen mode Exit fullscreen mode
    // After
    // app/checkout/layout.js
    export const metadata = {
      title: "Checkout page",
    };
    export default function CheckoutLayout({ children }) {
      return <>{children}</>;
    } 
Enter fullscreen mode Exit fullscreen mode

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).

You can make use of 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
Enter fullscreen mode Exit fullscreen mode
    // layout.js
    import { Suspense } from "react";
    import Loading from "./loading";
    export default function ProductLayout({ children }) {
      return <Suspense fallback={<Loading />}>{children}</Suspense>;
    }
Enter fullscreen mode Exit fullscreen mode


javascript

references

original article is here

Top comments (0)