NextJS: A Beginner Guide
Getting started
Create a new nextjs project
npx create-next-app@latest
Give a project name and accept default value. To run the project run
npm run dev
Routing
- All routes must be placed inside the app folder
- Every file that corresponds to the route must be named
page.jsx
orpage.tsx
- Each folder corresponds to the path segment in the browser URL
For the home page route in a folder app
create a file name page.tsx
export default function Home() {
return (<h1>
This is home page!
</h1>);
}
layout.tsx
file is for nav bar and footer which can be shared in all the pages of the app. In layout.tsx
file
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>
<header>This is header of the page</header>
{children}
<footer>This is footer of the page</footer>
</body>
</html>
)
}
To create a route /about
we need to create a folder named about
inside app
folder and create a file name page.tsx
in about
folder. This will be the route for /about
const About = ()=>{
return <>
<h2>This is about page!</h2>
</>
}
export default About;
Nested Routing
Nested route such as /blog
, /blog/first
and /blog/second
to achieve this routes:
for that we can create a folder named blog
and create a page.tsx
file for /blog
route
for /blog/first
we can create another folder named first
inside blog
folder and create a file page.tsx
same for /blog/second
. Create a folder inside blog
folder named second
and create a page.tsx
file in that second
folder.
If you want a layout that cover all routes start from /blog/*
then you can create a layout.tsx
file inside blog
folder and following code goes in that file:
import Link from "next/link"
export default function BlogLayout({
children,
}: {
children: React.ReactNode
}) {
return <section>
<div style={{display:"flex", justifyContent: "flex-start", gap:"2rem"}}>
<Link href="/blog/first">
First blog
</Link>
<Link href="/blog/second">
Second blog
</Link>
</div>
{children}</section>
}
Dynamic Route
Dynamic route such as /products/productId
where productId
will be dynamic and based on the productId
the page will show the product detail of that specific product. To create such a route let's create a route /products
where list of the products will be displayed and when user click on specific product it will trigger the /products/productId
route and show the detail of that particular product. Create a folder named products
and inside that folder create a file named page.tsx
and following code goes in that file:
import Link from "next/link";
export default function Products() {
const productList = [
{
name: "product1",
id: 1,
},
{
name: "product2",
id: 2,
},
{
name: "product3",
id: 3,
},
{
name: "product4",
id: 4,
},
];
return (
<>
{" "}
<div
style={{ display: "flex", justifyContent: "flex-start", gap: "2rem" }}
>
{productList.map((product) => {
return (
<Link key={product.id} href={`/products/${product.id}`}>
{product.name}
</Link>
);
})}
</div>
</>
);
}
Now, inside products
folder create another folder named [productId]
and inside that folder create page.tsx
file. this folder will be dynamic which holds dynamic productId
. In page.tsx
file following code goes:
type Prop = {
params: { productId: number };
};
type Product = {
id: number;
detail: string;
};
const productDetail = [
{
id: 1,
detail: "This is the detail of product 1",
},
{
id: 2,
detail: "This is the detail of product 2",
},
{
id: 3,
detail: "This is the detail of product 3",
},
{
id: 4,
detail: "This is the detail of product 4",
},
];
export default async function ProductDetail({ params }: Prop) {
const { productId } = await params;
const product = productDetail.find(
(product) => product.id === Number(productId)
);
return (
<>
{product ? (
<h2>{product.detail}</h2>
) : (
<h2>Product detail could not be found !</h2>
)}
</>
);
}
Catch-all segments and Optional Catch-all segments
If we want to achieve a route such as /docs/concept1/exapmle1
or /docs/concept2/example1/feature1
, we can use Catch-all segments
. For that we need to put folder name inside [...slug]
or for Optional Catch-all segments
inside [[...slug]]
. Now, let's create a folder docs
and inside that folder let's create another folder name [[...slug]]
. Here we will be doing Optional catch-all segments
. Let's create a file page.tsx
inside that folder.
import Link from "next/link";
export default function Docs({ params }: { params: { slug: string[] } }) {
let contain: string;
const getURLPath = (slugArray: string[], index = 0): string => {
if (index >= slugArray.length) return "";
return `/${slugArray[index]}/ ${getURLPath(slugArray, index + 1)}`;
};
if (!params?.slug?.length) {
contain = "This is Document page";
} else {
contain = `This is about: docs/${getURLPath(params.slug)}`;
}
return (
<>
<h3>{contain}</h3>
</>
);
}
In above code, we get convert the slugs into URL using getURLPath
function and display the URL.
Private folder
Private folder is the folder and all its subfolders that are excluded from routing. To make the folder, add an underscore (_) at the start of the folder name. e.g. _private
.
Route Groups
Route groups help to organize routes and project files without impacting the URL structure. To create a routing group, wrap the grouping folder in round brackets (), and this will be excluded from the URL.
(auth)
login
register
forgot_password
Layouts
While pages are route-specific UI components, a layout is UI that is shared between multiple pages in your app. To create a layout, default export a React component from a layout.tsx
file.
Every layout component needs a children
prop. The following layout will have navbar
in the header
section and footer
, and this will be shared across the app.
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body>
<header>
<nav
style={{
display: "flex",
justifyContent: "flex-start",
gap: "2rem",
}}
>
<Link href={"/"}>Home</Link>
<Link href="/about">About Us</Link>
<Link href="/blog">Blog</Link>
<Link href="/products">View Products</Link>
<Link href="/docs">Docs</Link>
<Link href={"/profile"}>Profile</Link>
</nav>
</header>
{children}
<footer>This is footer of the page</footer>
</body>
</html>
);
}
Next.js also allows nested layouts. The following image is the visual representation of a nested layout.
Multiple Root Layouts
If we want to have a different layout for different parts of our application, we can achieve this with the help of Route Group
. Route group helps to organize application structure without affecting URLS as well as helps to apply layouts selectively to specific parts of the application.
(auth)
login
register
forgot_password
layout.tsx // only for login, register and forgot_password route
Routing Metadata
The metadata API in Next.js lets us define metadata for each page. It ensures our content looks great when it's shared or indexed by search engines
Two ways to handle metadata in layout.tsx
or page.tsx
files:
- Export a static
metadata
object - Export a dynamic
generateMetadata
function
Configuring metadata
- Both
layout.tsx
andpage.tsx
can export metadata. Layout metadata applies to all its pages, while page metadata is specific to that page - Metadata should be on the server side
- Metadata follows a top-down order, starting from the root level
- When metadata exists in multiple places along a route, it merges together, with page metadata overriding layout metadata for matching properties
export const metadata = {
title: "About Us"
}
Dynamic Metadata
Dynamic metadata depends on dynamic information. Such as the current route parameters or external data. Dynamic Metadata can be achieved by exporting the ' generateMetadatafunction that returns a
Metadata` object.
import { Metadata } from "next";
type Prop = {
params: Promise<{ productId: number }>;
};
export const generateMetadata = async ({ params }: Prop): Promise<Metadata> => {
const { productId } = await params;
return {
title: `Product ${productId}`,
};
};
params and searchParams
params
is a promise that resolves to an object containing the dynamic route parameters, such as idsearchParams
is a promise that resolves to an object containing the query parameters such as filters, sorting
Let's create a following dynamic route in the `layout.tsx' file:
<Link href={"/news/123?lan=English"}>News</Link>
The above route is the dynamic route and has a news id 123
which is a params
and language of the content is English, which is searchParams
Now, let's create a folder with a name news
and another folder in this folder with a square bracket named [newsid], and in that folder create a file named page.tsx
.
import Link from "next/link";
type Prop = {
params: Promise<{ newsId: string }>;
searchParams: Promise<{ lan?: "English" | "Nepali" }>;
};
export default async function NewsPage({ params, searchParams }: Prop) {
const { newsId } = await params;
const { lan = "English" } = await searchParams;
return (
<>
<h2>News Feed in {lan}</h2>
<p>This is breaking news for news id {newsId}</p>
<Link href={`/news/${newsId}?lan=Nepali`}>Read in Nepali</Link>
<br></br>
<Link href={`/news/${newsId}?lan=English`}>Read in English</Link>
</>
);
}