Exploring Next.js new App Router

Adebisi TobilobaAdebisi Tobiloba
22 min read

The App Router is a new router introduced in NextJs v13, the new recommended way of navigating through webpages in NextJs.

It supports shared layouts, nested routing, loading states, error handling, and more.

The App Router works in a new directory named app unlike the old Router that worked in the pages directory

Image showing the app and pages directory. Gotten from nextJs docs

By default, the components inside app are React Server Components. This is a performance optimization that allows you to adopt them easily, and you can also use Client Components.

This article will go through its features

Prerequisites:

The reader should:

  • Know how React and NextJs work

  • Know the difference between server and client components

  • Have a good understanding of JavaScript, HTML, and CSS.

  • Have Node installed

What is routing?

Routing is the way we define and navigate through different pages of a website. It involves mapping URLs to certain resources in the application. It is the backbone of any application

What is the App router?

The app router is a router based on a file system, where:

  • Folders define routes

  • Files define the user interface (UI) to be shown for the route

How does routing work in the App Router?

Everything is built out from inside the app directory

And each folder represents a new route and URL segment

Special files used in App Router

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

extension for the following files can be in any of .js, .jsx, .ts or .tsx

  • layout: shared UI for a Folder and all its children. e.g., navbar, sidebar, or footer.

    In essence, this is where you should put any UI that you want to repeat for each folder within the folder layout file is located.

    On navigation, layouts preserve state, remain interactive, and do not re-render. Layouts can also be nested.

      export default function RootLayout({
        children, // every special file inside the folder
      }: {
        children: React.ReactNode
      }) {
        return (
          <html lang="en">
            <body>{children}</body>
          </html>
        )
      }
    

    A root layout (required) is defined at the top level of the app directory and applies to all routes. This layout enables you to modify the initial HTML returned from the server.

    Only the root layout can contain <html> and <body> tags.

  • page: The unique UI for a certain route and the only file publicly accessible from a URL. They are server components by default but can be set to client components.

  • loading: A placeholder UI for when the main UI is not yet ready, this is what shows up while the page file is doing something heavy like fetching data.

  • not-found: UI to bring up when a certain route is not found, this is what shows up whenever the user inputs an invalid URL

  • error: UI to bring up, whenever there's a runtime error in the folder it's defined in.

  • global-error: UI to catch all errors in the whole application, it's a variance of the error file

  • route: Server-side API endpoint, this is where all API endpoints that accept and respond to a user request are defined

  • template: Special type of layout file that re-renders on reload and for every nested route. Useful when the shared UI changes based on the route the user is in.

  • default: A fallback route for parallel routes. Explained better below

Special files hierarchy

The React components defined in special files of a route segment are rendered in a specific hierarchy:

  • layout

  • template

  • error (React error boundary)

  • loading (React suspense boundary)

  • not-found (React error boundary)

  • page or nested layout

Exploring App Router

Here are the features that come with App Router

  • Defining routes

    Routes are defined by creating folders in the app directory, You can create nested routes by nesting folders inside each other.

    Each Folder must have a page file to be publicly accessible.

  • Pages and layout

    For layout two main special files are used: page and layout

    • A page file is the UI unique to a route, it can't be passed down and a route won't be accessible without it.
    // `app/page.tsx` is the UI for the `/` URL
    export default function Page() {
      return <h1>Hello, Home page!</h1>
    }
    // `app/dashboard/page.tsx` is the UI for the `/dashboard` URL
    export default function Page() {
      return <h1>Hello, Dashboard Page!</h1>
    }
  • layout is a UI that's shared between multiple pages, that are nested. layout files can also be nested.

Every special file in a folder is wrapped by the layout file and is passed into it as the children props so anything put into the layout files shows on every route nested in the layout folder

    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}
          <footer></footer>
        </section>
      )
    }
  • Linking and navigation

    There are two ways to navigate between routes:

    • Link component

    • useRouter Hook

      Link Component: Works the same way as the HTML anchor <a> tag, but with some modifications so it works well with client-side navigation.

      • To use<Link>, import it from next/link, and pass a href prop to the component:
        import Link from 'next/link'

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

useRouter Hook: This is to be used when you want to link to a page programmatically or on condition.

To use useRouter, import it from next/navigation, and call the hook inside your Client Component:

        'use client'

        import { useRouter } from 'next/navigation'

        export default function Page() { 
        const router = useRouter() 
        const location = "dashboard"

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

The useRouter provides methods such as push(), refresh(), and more. See the API reference for more information.

  • Route groups

    Since nested folders are automatically mapped to URL paths, Route group provides a way to circumvent that.

    These folders are declared in parentheses (folderName).

    This helps in:

    • Organizing routes into groups without affecting the URL path.

  • Creating multiple nested layouts in the same segment

Dynamic routes

These are for routes for which you don't have an Idea of what the route name would be.

For example, routes that include IDs that are automatically generated.

It can be created by wrapping a folder's name in square brackets: [folderName]. For example, [id] or [slug].

Dynamic URL Segments are passed as the params prop to layout, page, route, and generateMetadata functions.

RouteExample URLparams
app/blog/[id]/page.js/blog/a{ id: 'a' }
app/blog/[id]/page.js/blog/b{ id: 'a' }
app/blog/[id]/page.js/blog/c{ id: 'a' }

Dynamic routes can be extended to capture nested dynamic segments by adding an ellipse [...folderName]

RouteExample URLparams
app/shop/[...slug]/page.js/shop/a{ slug: ['a'] }
app/shop/[...slug]/page.js/shop/a/b{ slug: ['a', 'b'] }
app/shop/[...slug]/page.js/shop/a/b/c{ slug: ['a', 'b', 'c'] }
  • Loading UI and streaming

    Loading State

    Every route while loading has a loading state that allows the use of a fallback UI that is shown immediately upon navigation. This helps users understand the app is responding and provides a better user experience.

    This state is handled by adding a loading.js file inside a folder.

    The loading file helps create a loading UI that shows up while the content of a route segment loads. The new content is automatically swapped in once rendering is complete.

    Streaming:

    This allows you to break down the page's HTML into smaller chunks and progressively send those chunks from the server to the client.

    This enables part of the page to be displayed sooner, without waiting for all the data to load before any UI can be rendered.

    This is made possible by using React's Suspense component, which works by wrapping a component that performs an asynchronous action (e.g. fetch data), showing fallback UI (e.g. skeleton, spinner) while it's happening, and then swapping in your component once the action completes.

import { Suspense } from 'react'
import { PostFeed, Weather } from './Components'

export default function Posts() {
  return (
    <section>
      <Suspense fallback={<p>Loading feed...</p>}>
        <PostFeed />
      </Suspense>
      <Suspense fallback={<p>Loading weather...</p>}>
        <Weather />
      </Suspense>
    </section>
  )
}

Streaming is particularly beneficial when you want to prevent long data requests from blocking the page from rendering as it can reduce the Time To First Byte (TTFB) and First Contentful Paint (FCP). It also helps improve Time to Interactive (TTI), especially on slower devices.

  • Error handling

    NextJs provides a simple way of handling errors by using the error file

    The error file allows you to gracefully handle unexpected runtime errors in nested routes.

'use client' // Error components must be Client Components

import { useEffect } from 'react'

export default function Error({
  error,
  reset,
}: {
  error: Error
  reset: () => void
}) {
  useEffect(() => {
    // Log the error to an error reporting service
    console.error(error)
  }, [error])

  return (
    <div>
      <h2>Something went wrong!</h2>
      <button
        onClick={
          // Attempt to recover by trying to re-render the segment
          () => reset()
        }
      >
        Try again
      </button>
    </div>
  )
}

The error file has access to the error message and reset function through props

  • error: The message detailing the error that crashed the app

  • reset: A function that when called reloads the affected component since the cause of an error can sometimes be temporary

To specifically handle errors in these root components, use a variation of error.js called global-error.js located in the root app directory.

Unlike the root error.js, the global-error.js error boundary wraps the entire application, and its fallback component replaces the root layout when active. Because of this, it is important to note that global-error.js must define its own <html> and <body> tags.

  • Parallel routes

    You can display two or more routes in the same layout. Without interrupting each other.

    Parallel Routing can be used to implement complex routing patterns.

    For example, for an admin dashboard page, you can simultaneously render the team and analytics pages.

    Parallel routes are created using named slots. Slots are defined with the @folder convention and are passed to the same-level layout as props.

export default function Layout(props: {
  children: React.ReactNode
  analytics: React.ReactNode
  team: React.ReactNode
}) {
  return (
    <>
      {props.children}
      {props.team}
      {props.analytics}
    </>
  )
}

Parallel Routes can be used to implement conditional routing. For example, you can render a @dashboard or @login route depending on the authentication state.

  • Intercepting routes

    Intercepting routes allows you to load a route within the current layout while keeping the context for the current page. This is very useful for modal.

    For Example, when clicking on a photo from within a feed, a modal overlaying the feed shows up with the photo. In this case, Next.js intercepts the /feed route and "masks" this URL to show /photo/123 instead.

    However, when navigating to the photo directly, for example, by clicking a shareable URL or refreshing the page, the entire photo page should render instead of the modal. No route interception should occur.

    Intercepting routes can be defined with the (..) convention, which is similar to the relative path convention ../ but for the URL segments.

    You can use:

    • (.) to match segments on the same level

    • (..) to match segments one level above

    • (..)(..) to match segments two levels above

    • (...) to match segments from the root app directory

For example, you can intercept the photo segment from within the feed segment by creating a (..)photo directory.

Intercepting routes folder structure

Note that the (..) convention is based on the URL, not the file system.

This is very good for creating a modal.

  • Route Handlers

    Route Handlers allow you to create custom request handlers for a given route using the Web Request and Response APIs

    Route.js Special File

    Route Handlers are defined in a route file inside the app directory, but there can't be a route file at the same route segment level as page.

    The following HTTP methods are supported: GET, POST, PUT, PATCH, DELETE, HEAD, and OPTIONS. If an unsupported method is called, Next.js will return a 405 Method Not Allowed response.

  • Middleware

    Middleware allows you to run code before a request is completed.

    Use the file middleware.ts (or .js) in the root of your project to define Middleware. For example, at the same level as pages or app, or inside src if applicable.

  • Project Organization

    Apart from routing folder and file conventions, Next.js is unopinionated about how you organize and colocate your project files

    This means you can arrange your files in any way that suits you

  • Internationalization

    Next.js enables you to configure the routing and rendering of content to support multiple languages. Making your site adaptive to different locales includes translated content (localization) and internationalized routes.

Building a NextJs app using the App Router

Here's a project using everything I've listed above.

  • Installation

    Before doing anything we have to install NextJs

    To install NextJS, we'll need the terminal.

    Copy the following code into your terminal: You must have node installed

npx create-next-app@latest

This helps set up your NextJS app. It'll ask for your choices while installing

What is your project named? my-app
Would you like to use TypeScript? No / Yes
Would you like to use ESLint? No / Yes
Would you like to use Tailwind CSS? No / Yes
Would you like to use `src/` directory? No / Yes
Would you like to use App Router? (recommended) No / Yes
Would you like to customize the default import alias? No / Yes

Choose the features you want to come with the NextJs app being created

I'll be installing: TypeScript, ESLint, and Tailwind CSS with my setup

Make sure to click yes for App Router (Line 6) Since it's what we're discussing

And wait for the process to complete, then open the created folder in the editor of your choice, I will be using Visual Studio Code (Vs code).

We'll be building a simple blog website and only implementing the routing features, to keep it simple.

Defining Routes

The website will have 3 main routes:

  • Blog: Contains a link to each blog post and a sample of each

  • Admin: Shows the blog route using the parallel routes feature and the ability to edit each blog can later be introduced

  • Photos: Route for images

  • Blog

This is where each blog page will be defined and it will contain

  • A dynamic route for each blog using the [id] identifier

  • A route interceptor inside the [id] route to the photos route to display a custom page when any image within it is clicked

  • Admin

This will contain a slot for the blog route, which will be displayed along with the admin route children

  • Photos

This will just contain a page file that renders images

And outside of the app directory, we will have a utils folder, which will contain our fake blog data which is just a JSON object

[
    {
        "id": 1,
        "mainContent": "Lorem ipsum dolor sit amet consectetur adipisicing elit. Maiores modi id, ipsam exercitationem ipsum laboriosam cum accusantium consequuntur quam neque sint quae consectetur ut eius vitae soluta aspernatur voluptate provident.Lorem ipsum dolor sit amet consectetur adipisicing elit. Maiores modi id, ipsam exercitationem ipsum laboriosam cum accusantium consequuntur quam neque sint quae consectetur ut eius vitae soluta aspernatur voluptate provident.",
        "others": "Lorem ipsum dolor sit amet consectetur adipisicing elit. Maiores modi id, ipsam exercitationem ipsum laboriosam cum accusantium consequuntur quam neque sint quae consectetur ut eius vitae soluta aspernatur voluptate provident.Lorem ipsum dolor sit amet consectetur adipisicing elit. Maiores modi id, ipsam exercitationem ipsum laboriosam cum accusantium consequuntur quam neque sint quae consectetur ut eius vitae soluta aspernatur voluptate provident.Lorem ipsum dolor sit amet consectetur adipisicing elit. Maiores modi id, ipsam exercitationem ipsum laboriosam cum accusantium consequuntur quam neque sint quae consectetur ut eius vitae soluta aspernatur voluptate provident.Lorem ipsum dolor sit amet consectetur adipisicing elit. Maiores modi id, ipsam exercitationem ipsum laboriosam cum accusantium consequuntur quam neque sint quae consectetur ut eius vitae soluta aspernatur voluptate provident.Lorem ipsum dolor sit amet consectetur adipisicing elit. Maiores modi id, ipsam exercitationem ipsum laboriosam cum accusantium consequuntur quam neque sint quae consectetur ut eius vitae soluta aspernatur voluptate provident.Lorem ipsum dolor sit amet consectetur adipisicing elit. Maiores modi id, ipsam exercitationem ipsum laboriosam cum accusantium consequuntur quam neque sint quae consectetur ut eius vitae soluta aspernatur voluptate provident.Lorem ipsum dolor sit amet consectetur adipisicing elit. Maiores modi id, ipsam exercitationem ipsum laboriosam cum accusantium consequuntur quam neque sint quae consectetur ut eius vitae soluta aspernatur voluptate provident.Lorem ipsum dolor sit amet consectetur adipisicing elit. Maiores modi id, ipsam exercitationem ipsum laboriosam cum accusantium consequuntur quam neque sint quae consectetur ut eius vitae soluta aspernatur voluptate provident."
    },
    {
        "id": 2,
        "mainContent": "Lorem ipsum dolor sit amet consectetur adipisicing elit. Maiores modi id, ipsam exercitationem ipsum laboriosam cum accusantium consequuntur quam neque sint quae consectetur ut eius vitae soluta aspernatur voluptate provident.Lorem ipsum dolor sit amet consectetur adipisicing elit. Maiores modi id, ipsam exercitationem ipsum laboriosam cum accusantium consequuntur quam neque sint quae consectetur ut eius vitae soluta aspernatur voluptate provident.",
        "others": "Lorem ipsum dolor sit amet consectetur adipisicing elit. Maiores modi id, ipsam exercitationem ipsum laboriosam cum accusantium consequuntur quam neque sint quae consectetur ut eius vitae soluta aspernatur voluptate provident.Lorem ipsum dolor sit amet consectetur adipisicing elit. Maiores modi id, ipsam exercitationem ipsum laboriosam cum accusantium consequuntur quam neque sint quae consectetur ut eius vitae soluta aspernatur voluptate provident.Lorem ipsum dolor sit amet consectetur adipisicing elit. Maiores modi id, ipsam exercitationem ipsum laboriosam cum accusantium consequuntur quam neque sint quae consectetur ut eius vitae soluta aspernatur voluptate provident.Lorem ipsum dolor sit amet consectetur adipisicing elit. Maiores modi id, ipsam exercitationem ipsum laboriosam cum accusantium consequuntur quam neque sint quae consectetur ut eius vitae soluta aspernatur voluptate provident.Lorem ipsum dolor sit amet consectetur adipisicing elit. Maiores modi id, ipsam exercitationem ipsum laboriosam cum accusantium consequuntur quam neque sint quae consectetur ut eius vitae soluta aspernatur voluptate provident.Lorem ipsum dolor sit amet consectetur adipisicing elit. Maiores modi id, ipsam exercitationem ipsum laboriosam cum accusantium consequuntur quam neque sint quae consectetur ut eius vitae soluta aspernatur voluptate provident.Lorem ipsum dolor sit amet consectetur adipisicing elit. Maiores modi id, ipsam exercitationem ipsum laboriosam cum accusantium consequuntur quam neque sint quae consectetur ut eius vitae soluta aspernatur voluptate provident.Lorem ipsum dolor sit amet consectetur adipisicing elit. Maiores modi id, ipsam exercitationem ipsum laboriosam cum accusantium consequuntur quam neque sint quae consectetur ut eius vitae soluta aspernatur voluptate provident."
    },
    {
        "id": 3,
        "mainContent": "Lorem ipsum dolor sit amet consectetur adipisicing elit. Maiores modi id, ipsam exercitationem ipsum laboriosam cum accusantium consequuntur quam neque sint quae consectetur ut eius vitae soluta aspernatur voluptate provident.Lorem ipsum dolor sit amet consectetur adipisicing elit. Maiores modi id, ipsam exercitationem ipsum laboriosam cum accusantium consequuntur quam neque sint quae consectetur ut eius vitae soluta aspernatur voluptate provident.",
        "others": "Lorem ipsum dolor sit amet consectetur adipisicing elit. Maiores modi id, ipsam exercitationem ipsum laboriosam cum accusantium consequuntur quam neque sint quae consectetur ut eius vitae soluta aspernatur voluptate provident.Lorem ipsum dolor sit amet consectetur adipisicing elit. Maiores modi id, ipsam exercitationem ipsum laboriosam cum accusantium consequuntur quam neque sint quae consectetur ut eius vitae soluta aspernatur voluptate provident.Lorem ipsum dolor sit amet consectetur adipisicing elit. Maiores modi id, ipsam exercitationem ipsum laboriosam cum accusantium consequuntur quam neque sint quae consectetur ut eius vitae soluta aspernatur voluptate provident.Lorem ipsum dolor sit amet consectetur adipisicing elit. Maiores modi id, ipsam exercitationem ipsum laboriosam cum accusantium consequuntur quam neque sint quae consectetur ut eius vitae soluta aspernatur voluptate provident.Lorem ipsum dolor sit amet consectetur adipisicing elit. Maiores modi id, ipsam exercitationem ipsum laboriosam cum accusantium consequuntur quam neque sint quae consectetur ut eius vitae soluta aspernatur voluptate provident.Lorem ipsum dolor sit amet consectetur adipisicing elit. Maiores modi id, ipsam exercitationem ipsum laboriosam cum accusantium consequuntur quam neque sint quae consectetur ut eius vitae soluta aspernatur voluptate provident.Lorem ipsum dolor sit amet consectetur adipisicing elit. Maiores modi id, ipsam exercitationem ipsum laboriosam cum accusantium consequuntur quam neque sint quae consectetur ut eius vitae soluta aspernatur voluptate provident.Lorem ipsum dolor sit amet consectetur adipisicing elit. Maiores modi id, ipsam exercitationem ipsum laboriosam cum accusantium consequuntur quam neque sint quae consectetur ut eius vitae soluta aspernatur voluptate provident."
    },
    {
        "id": 4,
        "mainContent": "Lorem ipsum dolor sit amet consectetur adipisicing elit. Maiores modi id, ipsam exercitationem ipsum laboriosam cum accusantium consequuntur quam neque sint quae consectetur ut eius vitae soluta aspernatur voluptate provident.Lorem ipsum dolor sit amet consectetur adipisicing elit. Maiores modi id, ipsam exercitationem ipsum laboriosam cum accusantium consequuntur quam neque sint quae consectetur ut eius vitae soluta aspernatur voluptate provident.",
        "others": "Lorem ipsum dolor sit amet consectetur adipisicing elit. Maiores modi id, ipsam exercitationem ipsum laboriosam cum accusantium consequuntur quam neque sint quae consectetur ut eius vitae soluta aspernatur voluptate provident.Lorem ipsum dolor sit amet consectetur adipisicing elit. Maiores modi id, ipsam exercitationem ipsum laboriosam cum accusantium consequuntur quam neque sint quae consectetur ut eius vitae soluta aspernatur voluptate provident.Lorem ipsum dolor sit amet consectetur adipisicing elit. Maiores modi id, ipsam exercitationem ipsum laboriosam cum accusantium consequuntur quam neque sint quae consectetur ut eius vitae soluta aspernatur voluptate provident.Lorem ipsum dolor sit amet consectetur adipisicing elit. Maiores modi id, ipsam exercitationem ipsum laboriosam cum accusantium consequuntur quam neque sint quae consectetur ut eius vitae soluta aspernatur voluptate provident.Lorem ipsum dolor sit amet consectetur adipisicing elit. Maiores modi id, ipsam exercitationem ipsum laboriosam cum accusantium consequuntur quam neque sint quae consectetur ut eius vitae soluta aspernatur voluptate provident.Lorem ipsum dolor sit amet consectetur adipisicing elit. Maiores modi id, ipsam exercitationem ipsum laboriosam cum accusantium consequuntur quam neque sint quae consectetur ut eius vitae soluta aspernatur voluptate provident.Lorem ipsum dolor sit amet consectetur adipisicing elit. Maiores modi id, ipsam exercitationem ipsum laboriosam cum accusantium consequuntur quam neque sint quae consectetur ut eius vitae soluta aspernatur voluptate provident.Lorem ipsum dolor sit amet consectetur adipisicing elit. Maiores modi id, ipsam exercitationem ipsum laboriosam cum accusantium consequuntur quam neque sint quae consectetur ut eius vitae soluta aspernatur voluptate provident."
    }
]

Your folder structure should look like this

With that out of the way, let's start coding

App directory (/)

  • Layout: Inside the layout file, we'll be defining a navbar that shows on every route
import "./globals.css";
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import Link from "next/link";

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

export const metadata: Metadata = {
  title: "Blog websiter",
  description: "Exploring the Next.js App Router",
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body className={inter.className}>
        <nav className="flex items-center justify-between p-2 ">
          <h1>
            <Link href="/">Home </Link>
          </h1>
          <ul className="flex gap-3">
            <li>
              <Link href="/blog"> Blogs </Link>
            </li>
            <li>
              <Link href="/admin"> Admin</Link>
            </li>
          </ul>
        </nav>
        {children}
      </body>
    </html>
  );
}
  • page: We'll just define a simple component that says Home
import React from "react";

const Home = () => {
  return <div>Home</div>;
};

export default Home;

Photos route (/photos)

  • page: This will just be a simple component that renders out an image of the next.js logo
import Image from "next/image";
import React from "react";

const page = () => {
  return (
    <div className="w-screen h-screen">
      <Image width={320} height={500} alt="image" src="/next.svg" />
    </div>
  );
};

export default page;

Blog Route (/blog)

  • layout : This is a simple layout that just adds the word blog on every route within the blog route
import React from "react";

const BlogLayout = ({ children }: { children: React.ReactNode }) => {
  return (
    <div className="w-screen h-screen">
      <div>blog</div>
      <div>{children}</div>
    </div>
  );
};

export default BlogLayout;
  • error: This was created to easily handle all the errors I ran into while building this project. It prints the error to the console and has a button I can click to retry. Add to your codebase if you like (I'll suggest adding it to every route)
"use client";
import React from "react";

const error = ({ error, retry }: { error: string; retry: void }) => {
  console.log(error);
  return <div onClick={() => retry}>retry</div>;
};

export default error;
  • page: This goes through our blogs data and renders out a list of the blogs which when clicked link to the blog page
"use client";
import React, { useRef } from "react";
import db from "@/utils/data.json";
import { useRouter } from "next/navigation";

const Blog = () => {
  const navigate = useRouter();
  const data = useRef(db);
  return (
    <>
      {data.current.length > 0 &&
        data?.current.map((blog) => {
          return (
            <div
              key={blog.id}
              className="flex flex-col gap-2 p-4 mb-3 border-2"
              onClick={() => {
                navigate.push(`/blog/${blog.id}`);
              }}
            >
              <h1>{blog.mainContent}</h1>
            </div>
          );
        })}
    </>
  );
};

export default Blog;

Blog page Route (/blog/[id])

  • (..)photos/page This is the UI for the interrupted link to the photos route, it displays the Vercel logo instead of the next.js logo. (This is just a very contrived example of the usage of the interrupted routes feature)
"use client";
import Image from "next/image";
import { useRouter } from "next/navigation";
import React from "react";

const page = () => {
  const router = useRouter();
  return (
    <div
      className="fixed top-0 bottom-0 left-0 right-0 z-10 mx-auto bg-black/60"
      onClick={(e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
        if ((e.target as HTMLDivElement).tagName === "DIV") {
          router.back();
        }
      }}
    >
      <div className="absolute w-full p-6 -translate-x-1/2 -translate-y-1/2 top-1/2 left-1/2 sm:w-10/12 md:w-8/12 lg:w-1/2">
        <Image width={320} height={500} alt="image" src="/vercel.svg" />
      </div>
    </div>
  );
};

export default page;
  • page This is the UI for each blog page, here I created a component Sleep that imitates fetching data that takes some time so to show how streaming works
"use client";
import db from "@/utils/data.json";
import Image from "next/image";
import Link from "next/link";
import React, { Suspense } from "react";

const Sleep = async ({
  blog,
}: {
  blog: { id: number; mainContent: string; others: string } | undefined;
}): Promise<React.JSX.Element> => {
  const test = await new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("hi");
    }, 5000);
  });
  return <p>{`${test} ${blog?.others}`}</p>;
};
const BlogPost = ({ params }: { params: { id: string } }) => {
  const allBlog = db;
  const blog = allBlog.find(({ id }) => id === +params.id);
  return (
    <div>
      <h1 className="text-2xl font-extrabold">{blog?.mainContent}</h1>

      <Suspense fallback={<p className="font-bold">loading</p>}>
        <Sleep blog={blog} />
      </Suspense>
      <Link href="/photos">
        <Image width={320} height={500} alt="image" src="/vercel.svg" />
      </Link>
    </div>
  );
};

export default BlogPost;

Admin Route (/admin)

  • Blog slot (@blog): This is the same code as the blog route defined above excluding the dynamic route [id] since we won't be needing it. And including a default file that is just a function that returns null as the slot fallback.
import React from "react";

const Default = () => {
  return null;
};

export default Default;
  • layout : A simple layout that places the blog slot and its children side by side
import React from "react";

const AdminLayout = ({
  children,
  blog,
}: {
  children: React.ReactNode;
  blog: React.ReactNode;
}) => {
  return (
    <div className="w-screen h-screen flex">
      <div className="w-full">{blog}</div>
      <div className="w-full">{children}</div>
    </div>
  );
};

export default AdminLayout;
  • page: to keep this project simple, it only contains a text telling the user to log in so as to be able to edit the blogs
import React from "react";

const Dashboard = () => {
  return <div>To your left are all your blogs please log in to edit it</div>;
};

export default Dashboard;

With that, we've defined all the routes the website needs and practised all of the App router's main features

For this website to be fully functional, it'll require a lot more but this setup

all its routing and showcase the App Router well

And those are the main features of the App Router, if you want to look more into NextJS, check out its documentation.

0
Subscribe to my newsletter

Read articles from Adebisi Tobiloba directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Adebisi Tobiloba
Adebisi Tobiloba

Software Developer | Technical Writer