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

Lada496
3 min readMay 28, 2023

--

Photo by Lautaro Andreani on Unsplash

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.jsapp/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
// 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);
// 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).
  • 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)
  • 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} />}
</>
);
};
// 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).

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
// layout.js
import { Suspense } from "react";
import Loading from "./loading";
export default function ProductLayout({ children }) {
return <Suspense fallback={<Loading />}>{children}</Suspense>;
}

references

--

--

Lada496
Lada496

No responses yet