Skip to content

React (next.js) Router

Introduction

Next.js ver 13 (oct 2022) introduced new router concept - App Router (src/app folder).
Old one is called Pages Router (still works, app router has higher priority on conflict case).

  • Folders are used to define routes.
  • Route is a single path of nested folders,
  • Final element is page.tsx (it's like index.html)

Pages

Create page.tsx inside any folder to make route accessible.

Layouts

A layout is UI that is shared between multiple routes. On navigation, layouts preserve state, remain interactive, and do not re-render. Layouts can also be nested.

Layout.tsx component should accept a children prop that will be populated with a child layout (if it exists) or a page during rendering.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
export default function DashboardLayout({
  children, // will be a page or nested layout
}: {
  children: React.ReactNode
}) {
  return (
    <section>
      {/* Include shared UI here e.g. a header or sidebar */}
      <nav></nav>

      {children}
    </section>
  )
}
  • Only the root layout can contain <html> and <body> tags.
  • When a layout.js and page.js file are defined in the same folder, the layout will wrap the page.

Metadata can only be defined in the root layout.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import type { Metadata } from "next";
import { Inter } from "next/font/google";

const inter = Inter({ subsets: ["latin"] });

export const metadata: Metadata = {
  title: "Create Next App",
  description: "Generated by create next app",
};

export default function RootLayout({
  children,
}: Readonly<{children: React.ReactNode;}>) {
  return (
    <html lang="en">
      <body className={inter.className}>{children}</body>
    </html>
  );
}

Linking

  • <Link> Component
  • useRouter hook (Client Components)
  • redirect function (Server Components)
  • native History API
1
2
3
4
5
import Link from 'next/link'

export default function Page() {
  return <Link href="/dashboard">Dashboard</Link>
}

Use usePathname() to determine if a link is active. For example, to add a class to the active link, you can check if the current pathname matches the href of the link.

1
2
3
4
5
6
7
8
9
export function Links() {
  const pathname = usePathname()

  return (
    <li>
        <Link className={`link ${pathname === '/' ? 'active' : ''}`} href="/">
        Home
        </Link>
    </li>

The useRouter hook allows you to programmatically change routes from Client Components.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
'use client'

import { useRouter } from 'next/navigation'

export default function Page() {
  const router = useRouter()

  return (
    <button type="button" onClick={() => router.push('/dashboard')}>
      Dashboard
    </button>
  )
}

Component hierarchy

Next.js provides a set of special files to create UI with specific behavior in nested routes.

  • layout
    • Shared UI for a segment and its children
  • page
    • Unique UI of a route and make routes publicly accessible
  • loading
    • Loading UI for a segment and its children
  • not-found
    • Not found UI for a segment and its children
  • error
    • Error UI for a segment and its children
  • global-error
    • Global Error UI
  • route
    • Server-side API endpoint
  • template
    • Specialized re-rendered Layout UI
  • default
    • Fallback UI for Parallel Routes

Types

Only the contents returned by page.tsx or route.tsx are publicly addressable.

Dynamic routes

A Dynamic Segment can be created by wrapping a folder's name in square brackets: [folderName].
Dynamic Segments are passed as the params prop to layout, page, route, and generateMetadata functions.

Catch-all Segments Dynamic Segments can be extended to catch-all subsequent segments by adding an ellipsis inside the brackets [...folderName]. Catch-all Segments can be made optional by including the parameter in double square brackets: [[...folderName]].

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import Link from 'next/link'

export default function PostList({ posts }) {
  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>
          <Link href={`/blog/${post.slug}`}>{post.title}</Link>
        </li>
      ))}
    </ul>
  )
}