Quick Overview of Next.js 13 Routing System

ymc9 - Dec 1 '22 - - Dev Community

Next.js 13 has landed in a somewhat confusing way. Many remarkable things have been added; however, a good part is still Beta. Nevertheless, the Beta features give us important signals on how the future of Next.js will be shaped, so there are good reasons to keep a close eye on them, even if you're going to wait to adopt them.

This article is part of a series of experiences about the Beta features. Let's explore the new routing system today.


Next.js 13 introduced a brand new app folder with a completely renovated routing system. It includes many improvements, and among all, the best gift is the new layout mechanism.

The app folder can coexist with the old pages folder as long as their routes don't clash, which allows you to adopt progressively. This post will cover the most notable new stuff with the app folder.

New folder structure

The app folder makes defining routes more explicit by requiring every route to be a folder. A route folder typically contains the following route files (with .js|.ts|.jsx|.tsx suffix):

  • page

Provides UI specific for this route.

  • layout

Provides layout UI for this route and all descendant routes.

  • loading

Provides a loading UI when server components for the route are being loaded - more on this in the next section.

  • error

Provides UI for dealing with errors within and under this route (unless handled by another error route in a descendant layer).

For more information about server components, check out the post below:

Layout and nesting

The renovated folder structure sets a good foundation for introducing more convention-based route files. As a result, it's now much easier to create, share and nest layouts in Next.js than ever.

Let's see what got changed.

In previous versions

Before Next.js 13, the officially recommended approach for creating a nested layout is for each Page to "declare" its layout, compose nesting, and then apply them at the top level in pages/_app.

// pages/index.js

export default function Page() {
  return (
    /** Your content */
  )
}

Page.getLayout = function getLayout(page) {
  return (
    <Layout>
      <NestedLayout>{page}</NestedLayout>
    </Layout>
  )
}
Enter fullscreen mode Exit fullscreen mode
// pages/_app.js

export default function MyApp({ Component, pageProps }) {
  // Use the layout defined at the page level, if available
  const getLayout = Component.getLayout || ((page) => page)
  return getLayout(<Component {...pageProps} />)
}
Enter fullscreen mode Exit fullscreen mode

Although this approach results in optimal performance (because layouts are not remounted when changing route), it's cumbersome and unnatural, especially compared to what react-router offers.

With Next.js 13

The new routing system makes "layout" a first-class citizen. At any level of folders under app (i.e., any level of routing), you can use a layout.tsx to explicitly define a container component. This container component will automatically wrap around all pages in and under that folder. If there're multiple layout.tsx components at different levels, a wrapping hierarchy is automatically constructed:


https://beta.nextjs.org/docs/routing/pages-and-layouts

 

Much more straightforward than the old getLayout hack, isn't it?

A "grouping" feature is also available for you to create "logical" groups of pages to share the same layout without introducing an extra routing layer:


https://beta.nextjs.org/docs/routing/defining-routes#route-groups

 

As you can see, although /about and /blog don't share the same route prefix, they can share the same layout by residing in the same route group. Who doesn't like a bit more flexibility 👻?

Other notes

  1. Passing data from layout to children

    You can't do this. But it is not as serious a problem as it may seem because the new data fetching mechanism in Next.js 13 has built-in dedupe and caching, so it doesn't harm to fetch data in layout and its children components repeatedly.
    I'll cover the new data-fetching system in a future post.

  2. Breaking out of layout hierarchy

    There isn't a way for a page to "escape" the layout ancestors in the routing hierarchy. You'll have to design your routes or route groups carefully. In comparison, SvelteKit does provide better flexibility on this.

Error handling

Error handling is a small but convenient feature. The idea is quite simple:

  1. React has an Error Boundaries concept for capturing errors in a structured way.
  2. Next.js's route folders naturally form a component hierarchy.
  3. Why don't we invent a convention to handle errors at any level of routing?

That's what the error.tsx file does. It's nothing but syntactic sugar for wrapping the descendent component tree within a <ErrorBoundary />:


https://beta.nextjs.org/docs/routing/error-handling

 

With that, errors are localized to parts of UI rendered by sub-routes without affecting the display and interactivity of any other parts.

In the End

Thank you for your time reading to the end ❤️.

This is the second article of a series about the Beta features of Next.js 13. You can find the entire series here. If you like our posts, remember to follow us for updates at the first opportunity.


P.S. We're building ZenStack — a toolkit for developing secure CRUD apps with Next.js + Typescript. Our goal is to let you save time writing boilerplate code and focus on building what matters — the user experience.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .