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 or page.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.

Image description

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.

Image description

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:

  1. Export a static metadata object
  2. Export a dynamic generateMetadata function

Configuring metadata

  • Both layout.tsx and page.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 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

  • params is a promise that resolves to an object containing the dynamic route parameters, such as id
  • searchParams 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>
    </>
  );
}

Comments

No comments