users products
products/[productId]
products/[productId]/reviews/[reviewId]
docs/[...slug]
docs/[[...slug]]
_lib
(auth)
page.tsx and export default
not-found.tsx, notFound() method call
layout.tsx
template.tsx (init state variables on every render)
loading.tsx
error.tsx
Error Recovery with {reset} callback
metadata object
generatedMetadata function object
<Link href="/products">Go to Products<Link/>
useRouter().push("/goHere");
"use client";
@notifications slots as props in layout.tsx of parent
@notifications/archived (unmatched routes) and default.tsx
intercepting routes with (.) folders
A React Framework for building production-ready web applications. Everything is built-in: routing, optimized rendering, data fetching, bundling, compiling, SEO, and more.
Components are by-default server components here, unless explicitly marked as client component using use client
at the top.
Folder Structure - Every route goes inside the src/app
folder, layout.tsx
provides reusable UI structure to page.tsx
(route specific page)
Directory based routing (like Hugo’s content folder) - route hierarchy is represented by folders
Rules:
page.tsx
is a route pagesrc/app
folderRoute: the page.tsx
file must have atleast one React component (function) that must be an export default
export default function Products() { // component name doesn't matter
return <h1>Sample Product</h1>;
}
Nested Routes: /users
and /products
- create a folder for each route having their own page.tsx
.
Dynamic Routes - products/2
- create a folder called [productId]
and in its page.tsx
use params
variable to access URL path variable:
export default function Products({ params }) {
return <h1> This product is = { params.productId } </h1>;
}
Nested Dynamic Routes: /products/5/reviews/8
- create separate folders for products
and reviews
and do dynamic routing inside each using [productId]
and [reviewId]
folders
Catch-all Segments: /docs/feature1/concept1/example1
- create a folder docs/[...slug]
(can be named anything) and we can access segments in code as:
params.slug.length // 3
params.slug[0] // feature1
params.slug[1] // concept1
params.slug[2] // example1
But /docs
will fail now as it doesn’t have its own page.tsx
. We can use [[...slug]]
as an optional catch-all which matches /docs
too including everything above.
Either create a global not-found.tsx
and place in app
folder root.
// invoke it anywhere in the component
import { notFound } from "next/navigation";
// in component function
if(params.reviewId > 999){
notFound();
}
Or create a local one, placed in the route folder (app/reviews
). Local one will be given precedence over the global one.
We can have multiple files in a route folder but there can be only one page.tsx
We can have multiple functions in the page.tsx
but atleast one has to be export default
(only this will rendered)
Private Folders: name a route folder as _foobar
and it and all its subfolders are excluded from routing
Route Group: name a folder as (foobar)
and it won’t need its own segment in the URL (transparent) when accessing subfolder routes (used to group related routes together for programmer ease)
Place common HTML tags in a layout.tsx
file and it will be applied to a route and all its children. It is also applied to not-found.tsx
, error.tsx
, etc files.
The layout.tsx
file in app
folder (mandatory) allow common HTML tags such as <header>
and <footer>
placed in the file, to display on all it and its children route pages too.
Nested Layout: create layout.tsx
file in a route’s folder and it will be applied to it and all its children routes. Do note that any other layout of this route’s parent will also be applied to its children.
Route Group Layout: as as good developer practice, place a layout.tsx
file in a route group folder (auth)
to scope layout to only certain route folders grouped logically
Place objects in either layout.tsx
or page.tsx
and Next generates HTML tags automatically. The latter takes precedence if there is ambiguity.
Static Metadata Object: create metadata
object in the page file
export const metadata = {
title: "Foobar::Home",
description: "Homepage of Foobar"
};
They will be rendered as <title>
and <meta name="description" content="Homepage of Foobar">
in the browser.
Dynamic Metadata: create generatedMetadata
function object in the page file
import { Metadata } from "next";
export const generatedMetadata = ({ params }): Metadata => {
return {
title: `Product ${params.productId}`
};
};
Set titles in the above two approaches, either as string
type (shown above) or Metadata
type (shown below).
import { Metadata } from "next";
export const metadata: Metadata = {
title: {
absolute: "Override both the below titles to display this one",
default: "Fallback to this title if no title is defined in layout.tsx or page.tsx",
template: "%s | Get the value before pipe from children's title field"
}
};
import Link from "next/link";
// in JSX
// static link
<Link href="/products">Go to Products<Link/>
// dynamic link
let productId = 100;
<Link href=`/products/${productId}`>Go to {productId}<Link/>
// replace history; on back button click we go back to Home and not the previous page
<Link href="/products" replace>Go to Products<Link/>
Use useRouter
hook in a client component.
"use client";
import { useRouter } from "next/navigation";
// in component function
const router = useRouter();
const handleClick = () => {
console.log("Redirecting to Home...");
router.push("/");
// other methods
router.replace("/"); // reset browser stack history and goto Home
router.back(); // goto previous page in browser stack
router.forward(); // goto next page in browser stack
}
Any state variable displayed on the screen in the layout.tsx
doesn’t reset value when navigating between routes, it stays on the screen!
Use template.tsx
if we want to have new state variable initialized for every route we goto. Everything else is same as layout.
Use loading.tsx
file placed in route folder to display temporary content till actual content loads.
Use error.tsx
file placed in route folder with Client Component. Can receive an optional error
prop.
"use client";
export default function ErrorBoundary( { error } ){
return <div> { error.message } </div>
}
Any errors in the child components too will display this page.
Error in Layouts - any error thrown in layout.tsx
won’t be handled by the same folder’s error.tsx
. Place error file in parent folder to catch errors in subfolder layouts.
Use the provided reset
callback function variable to re-render route page, if there is no errors on re-render the new route page is displayed instead of error page.
"use client";
export default function ErrorBoundary( { error, reset } ){
return <button onClick = { reset }>Try again</button>
}
// in Dashboard component
<Foo \>
<Bar \>
<Foobar \>
Instead of composing components like above, we can use them as “slots” to divide page into sections - every slot is a mini-app with its own error handling, sub-navigation, and state management.
Create route folders named as @foobar
called “slots” to make parallel routes. They are automatically passed to the parent route folder’s layout.tsx
as props (no need to import). The additional children
prop is nothing but content from dashboard/page.tsx
.
/*
\dashboard
@analytics
@notifications
@users
*/
// in dashboard/layout.tsx
export default function MyDashboardLayout( { children, analytics, notifications, users } ){ // notice 4 slots
return <div> {children} </div>
}
They aren’t directly accessible from the browser even if we use their name in URL segments.
We can create normal route folders archived
in a slot route folder @notifications
and then we can route from localhost:8080/dashboard
to localhost:8080/dashboard/archived
(notice no notifications in URL).
Unmatched Routes: if this folder exists for under this slot folder only and not for other parallel slots:
default.tsx
for every slot including the children
slot// in dashboard/layout.tsx
export default function MyDashboardLayout( { children, users, guests } ){
const isLoggedIn = true;
return isLoggedIn ?
( <div> { children } </div> ) :
( guests );
}
Open another page when user wants to go to a page.
Create folders with (.)foobar
and display its page.tsx
when we go from current route to foobar
route. Subsequently on a reload, display actual route page rather than the intercept page.
(.) to match segments on the same level
(..) to match segments one level above
(...) to match segments from the root app dir
Modals can be created using these two concepts - disaply image by intercepting single image item page (photo-gallery/9
) from the main gallery page which contains a slot (@modal
).
\photo-gallery
\[id]
\@modal
\(..)photo-gallery/[id]
page.tsx
default.tsx