Exploring Next.js new App Router
Table of contents
- Prerequisites:
- What is routing?
- What is the App router?
- How does routing work in the App Router?
- Special files used in App Router
- Exploring App Router
- Defining routes
- Pages and layout
- Linking and navigation
- Route groups
- Dynamic routes
- Loading UI and streaming
- Error handling
- Parallel routes
- Intercepting routes
- Route Handlers
- Middleware
- Project Organization
- Internationalization
- Building a NextJs app using the App Router
- Installation
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
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 URLerror:
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 theerror
fileroute:
Server-side API endpoint, this is where all API endpoints that accept and respond to a user request are definedtemplate:
Special type oflayout
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 nestedlayout
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
andlayout
- 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.
- A
// `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
componentuseRouter
HookLink 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 fromnext/link
, and pass ahref
prop to the component:
- To use
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.
Route | Example URL | params |
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]
Route | Example URL | params |
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
fileThe
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 rootapp
directory
For example, you can intercept the photo
segment from within the feed
segment by creating a (..)photo
directory.
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 Handlers are defined in a
route
file inside theapp
directory, but there can't be aroute
file at the same route segment level aspage
.The following HTTP methods are supported:
GET
,POST
,PUT
,PATCH
,DELETE
,HEAD
, andOPTIONS
. If an unsupported method is called, Next.js will return a405 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 aspages
orapp
, or insidesrc
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]
identifierA route interceptor inside the
[id]
route to thephotos
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 componentSleep
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 adefault
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.
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