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.jsxorpage.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
metadataobject - Export a dynamic
generateMetadatafunction
Configuring metadata
- Both
layout.tsxandpage.tsxcan 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 aMetadata` 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
paramsis a promise that resolves to an object containing the dynamic route parameters, such as idsearchParamsis 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>
</>
);
}