🖍️
Developer Note
  • Welcome
  • Git
    • Eslint & Prettier & Stylelint & Husky
  • Programming Language
    • JavaScript
      • Script Async vs Defer
      • Module
      • Const VS Let VS Var
      • Promise
      • Event Loop
      • Execution Context
      • Hoisting
      • Closure
      • Event Buddling and Capturing
      • Garbage Collection
      • This
      • Routing
      • Debounce and Throttle
      • Web Component
      • Iterator
      • Syntax
      • String
      • Array
      • Object
      • Proxy & Reflect
      • ProtoType
      • Class
      • Immutability
      • Typeof & Instanceof
      • Npm (Node package manager)
    • TypeScript
      • Utility Type
      • Type vs Interface
      • Any vs Unknown vs Never
      • Void and undefined
      • Strict Mode
      • Namespace
      • Enum
      • Module
      • Generic
    • Python
      • Local Development
      • Uv
      • Asyncio & Event loop
      • Context Manager
      • Iterator & Generator
      • Fast API
      • Pydantic & Data Class
    • Java
      • Compilation and Execution
      • Data Type
      • Enumeration
      • Data Structure
      • Try Catch
      • InputStream and OutputStream
      • Concurrent
      • Unicode Block
      • Build Tools
      • Servlet
      • Java 8
  • Coding Pattern
    • MVC vs MVVM
    • OOP vs Functional
    • Error Handling
    • MVC vs Flux
    • Imperative vs Declarative
    • Design Pattern
  • Web Communication
    • REST API
      • Web Hook
      • CORS issue
    • HTTPS
    • GraphQL
      • REST API vs GraphQL
      • Implementation (NodeJS + React)
    • Server-Sent Event
    • Web Socket
    • IP
    • Domain Name System (DNS)
  • Frontend
    • Progressive Web App (PWA)
    • Single Page & Multiple Page Application
    • Search Engine Optimiaztion (SEO)
    • Web bundling & Micro-frontend
      • Webpack
        • Using Webpack to build React Application
        • Using Webpack to build react library
      • Vite
      • Using rollup to build react library
      • Implementing micro frontend
    • Web Security
      • CSRF & Nonce
      • XSS
      • Click hijacking
    • Cypress
    • CSS
      • Core
        • Box Model
        • Inline vs Block
        • Flexbox & Grid
        • Pseudo Class
        • Position
      • Tailwind CSS
        • Shadcn
      • CSS In JS
        • Material UI
    • React
      • Core
        • Component Pattern
        • React Lazy & Suspense
        • React Portal
        • Error Boundary
        • Rendering Methods
        • Environment Variable
        • Conditional CSS
        • Memo
        • Forward Reference
        • High Order Component (HOC) & Custom Hook
        • TypeScript
      • State Management
        • Redux
        • Recoil
        • Zustand
      • Routing
        • React Router Dom
      • Data Fetching
        • Axios & Hook
        • React Query
        • Orval
      • Table
        • React Table
      • Form & Validation
        • React Hook Form
        • Zod
      • NextJS
        • Page Router
        • App Router
      • React Native
    • Angular
    • Svelte
      • Svelte Kit
  • Backend
    • Cache
      • Browser Cache
      • Web Browser Storage
      • Proxy
      • Redis
    • Rate limit
    • Monitoring
      • Logging
      • Distributed Tracing
    • Load Test
    • Encryption
    • Authentication
      • Password Protection
      • Cookie & Session
      • JSON Web Token
      • SSO
        • OAuth 2.0
        • OpenID Connect (OIDC)
        • SAML
    • Payment
      • Pre-built
      • Custom
    • File Handling
      • Upload & Download (Front-end)
      • Stream & Buffer
    • Microservice
      • API Gateway
      • Service Discovery
      • Load Balancer
      • Circuit Breaker
      • Message Broker
      • BulkHead & Zipkin
    • Elastic Search
    • Database
      • SQL
        • Group By vs Distinct
        • Index
        • N + 1 problem
        • Normalization
        • Foreign Key
        • Relationship
        • Union & Join
        • User Defined Type
      • NOSQL (MongoDB)
      • Transaction
      • Sharding
      • Lock (Concurrency Control)
    • NodeJS
      • NodeJS vs Java Spring
      • ExpressJS
      • NestJS
        • Swagger
        • Class Validator & Validation Pipe
        • Passport (Authentication)
      • Path Module
      • Database Connection
        • Integrating with MYSQL
        • Sequalize
        • Integrating with MongoDB
        • Prisma
        • MikroORM
        • Mongoose
      • Streaming
      • Worker Thread
      • Passport JS
      • JSON Web Token
      • Socket IO
      • Bull MQ
      • Pino (Logging)
      • Yeoman
    • Spring
      • Spring MVC
      • Spring REST
      • Spring Actuator
      • Aspect Oriented Programming (AOP)
      • Controller Advice
      • Filter
      • Interceptor
      • Concurrent
      • Spring Security
      • Spring Boot
      • Spring Cloud
        • Resilience 4j
      • Quartz vs Spring Batch
      • JPA and Hibernate
      • HATEOS
      • Swagger
      • Unit Test (Java Spring)
      • Unit Test (Spring boot)
  • DevOp
    • Docker
    • Kubernetes
      • Helm
    • Nginx
    • File System
    • Cloud
      • AWS
        • EC2 (Virtual Machine)
        • Network
        • IAM
          • Role-Service Binding
        • Database
        • Route 53
        • S3
        • Message Queue
        • Application Service
        • Serverless Framework
        • Data Analysis
        • Machine Learning
        • Monitoring
        • Security
      • Azure
        • Identity
        • Compute Resource
        • Networking
        • Storage
        • Monitoring
      • Google Cloud
        • IAM
          • Workload Identity Federation
        • Compute Engine
        • VPC Network
        • Storage
        • Kubernetes Engine
        • App Engine
        • Cloud function
        • Cloud Run
        • Infra as Code
        • Pub/Sub
    • Deployment Strategy
    • Jenkins
    • Examples
      • Deploy NextJS on GCP
      • Deploy Spring on Azure
      • Deploy React on Azure
  • Domain Knowledge
    • Web 3
      • Blockchain
      • Cryptocurrency
    • AI
      • Prompt
      • Chain & Agent
      • LangChain
      • Chunking
      • Search
      • Side Products
Powered by GitBook
On this page
  • Introduction
  • Server & Client Side Component
  • Example
  • Result
  • Link Navigation
  • Backend API
  • Caching
  • Request memoization
  • Data Cache
  • Layout
  • Not Found & Error Boundary Page
  • Route Folder & File naming
  • Loading
  • Parallel Route

Was this helpful?

  1. Frontend
  2. React
  3. NextJS

App Router

Introduction

  • App router is mixture of multiple page and single page application

  • Each path has their own html page but when using next/link, the javascript will be loaded instead of html file

  • The initial page will be rendered on server, but for user interaction (e.g: useState, useEffect) , the page is needed to be hydrated with javascript file

Server & Client Side Component

  • When html page of Nextjs is loaded, the static content and data of of server component will be pre-rendered and fetched

  • The data of client component will then be hydrated through built javascript file

Example

layout.tsx
"use client";
// import type { Metadata } from "next";
import localFont from "next/font/local";
import "./globals.css";

const geistSans = localFont({
  src: "./fonts/GeistVF.woff",
  variable: "--font-geist-sans",
  weight: "100 900",
});
const geistMono = localFont({
  src: "./fonts/GeistMonoVF.woff",
  variable: "--font-geist-mono",
  weight: "100 900",
});

// export const metadata: Metadata = {
//   title: "Create Next App",
//   description: "Generated by create next app",
// };
export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode;
}>) {
  return (
    <html lang="en">
      <body
        className={`${geistSans.variable} ${geistMono.variable} antialiased`}
      >
        client layout
        {children}
      </body>
    </html>
  );
}
import ClientComponent from "./components/ClientComponent";
import ServerComponent from "./components/ServerComponent";

export default function Home() {
  return (
    <>
      <ClientComponent/>
      <ServerComponent/>
    </>
  );
}
ClientComponent.tsx
"use client";

import React from "react";

// Next.js fetch API in action
async function loadPosts() {
  const res = await fetch("https://jsonplaceholder.typicode.com/posts");

 // return res.json();
 return ["this is client component"];
}

const ClientComponent =  () => {
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    const [posts, setPosts] = React.useState<any[]>([]);
    React.useEffect(() => {
      loadPosts().then((posts) => setPosts(posts));
    }, []);
    return (

    <div className="post-list">
     Client Component
      {posts.map((post) => (
        <div key={post} className="post-listing">
          <p className="post-body">{post}</p>
        </div>
      ))}
    </div>
  );
};

export default ClientComponent;
ServerCompoenent.tsx
// Next.js fetch API in action
async function loadPosts() {
  const res = await fetch("https://jsonplaceholder.typicode.com/posts");
   /* 
   * Since this is a server component, the below message  
   * won't be displayed in the browser's dev console.
   */ 
  console.log("Server Component fetching");
  // return res.json();
  return ["this is server component"];
}

// Next.js will invalidate the cache when a
// request comes in, at most once every 60 seconds.
export const revalidate = 60

const ServerComponent = async () => {
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  const posts:any[] = await loadPosts();
  return (
    <div className="post-list">
      Server Component
      {posts.map((post) => (
        <div key={post} className="post-listing">
          <p className="post-body">{post}</p>
        </div>
      ))}
    </div>
  );
};

export default ServerComponent;

Result

Link Navigation

import Link from "next/link"
import ClientComponent from "../components/ClientComponent"

const OtherPage = () => {
  return (
    <div>
        <ClientComponent/>
        <Link href={"/"}>Back Home</Link>
    </div>
  )
}

export default OtherPage
  • If using Link instead of a tag for navigation. On UI level, it will be shown as a tag, but it will execute client side navigation behind the scene,

  • If the page contains server component, only RSC payload will be fetched, which is the result of server-side rendering and the position of client-side component with corresponding javascript file

Backend API

  • Nextjs can be used to act as full stack development, here is the example of implementing backend part. The file must be route.ts/js

api/route.ts
import { NextResponse } from 'next/server';

export function GET() {
  const randomNumber = Math.floor(Math.random() * 1000);
  const message = { message: `${randomNumber}` };
  return NextResponse.json(message);   
}
// Next.js fetch API in action
async function loadPosts() {
      const response = await fetch('http://localhost:3000/api');
      const json = await response.json();
      return await json.message;
    }

const ServerComponent = async () => {
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  const posts:any = await loadPosts();
  return (
    <div className="post-list">
     Server Component
        <div className="post-listing">
          <p className="post-body">{posts}</p>
        </div>
      
    </div>
  );
};
export default ServerComponent;

Caching

  • When using fetch api on Nextjs, there are several caches existing - request memoization, data cache

Request memoization

  • If you need to use the same data across a route, e.g using the same component, you do not have to fetch data in every time. Instead, it will fetch data once.

  • The cache lasts the lifetime of a server request until the React component tree has finished rendering.

import Link from "next/link";
import ServerComponent from "./components/ServerComponent";
export default function Home() {
  
  return (
    <>
      <ServerComponent/>
      <ServerComponent/>
      <Link href="/other">Go to other</Link>
    </>
  );
}
// Next.js fetch API in action
async function loadPosts() {
      const response = await fetch('http://localhost:3000/api',
        {cache:"default", next:{ revalidate: 0 }}
      );
      const json = await response.json();
      return await json.message;
    }

const ServerComponent = async () => {
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  const posts:any = await loadPosts();
  return (
    <div className="post-list">
     Server Component
        <div className="post-listing">
          <p className="post-body">{posts}</p>
        </div>
      
    </div>
  );
};
export default ServerComponent;
  • To disable request memoization, we need integrate with AbortController

// Next.js fetch API in action
async function loadPosts() {
      const { signal } = new AbortController();
      const response = await fetch('http://localhost:3000/api',
        {signal, cache:"default", next:{ revalidate: 0 }}
      );
      const json = await response.json();
      return await json.message;
    }

const ServerComponent = async () => {
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  const posts:any = await loadPosts();
  return (
    <div className="post-list">
     Server Component
        <div className="post-listing">
          <p className="post-body">{posts}</p>
        </div>
      
    </div>
  );
};
export default ServerComponent;

Data Cache

  • If fetch data with caching, the page will be built as static content (Static Generation), otherwise, the page will be built as dynamic

Cache options

fetch(`https://...`, { cache: 'default' | 'force-cache' | 'no-store' })
  • default: Nextjs fetching will be treated same as force-cache, unless revaildate is specified

  • no-store: Next.js fetches the resource from the remote server on every request without looking in the cache, and it will not update the cache with the downloaded resource.

  • force-cache: Next.js looks for a matching request in its Data Cache.

    • If there is a match and it is fresh, it will be returned from the cache.

    • If there is no match or a stale match, Next.js will fetch the resource from the remote server and update the cache with the downloaded resource.

Incremental Static Regeneration (ISR)

  • The data of cache will be stale after a period of time, and the data will be obtained from data source rather than cache

// Next.js fetch API in action
async function loadPosts() {
    const { signal } = new AbortController();
    const response = await fetch('http://localhost:3000/api',
      {signal,  next:{ revalidate: 5 }}
    );
      const json = await response.json();
      return await json.message;
    }

const ServerComponent = async () => {
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  const posts:any = await loadPosts();
  return (
    <div className="post-list">
     Server Component
        <div className="post-listing">
          <p className="post-body">{posts}</p>
        </div>
      
    </div>
  );
};
export default ServerComponent;

Layout

  • The component in the layout file can be shared in the same directory and its nested page file

  • SideMenu is common use case to put it into layout file

app/layout.tsx
 "use client"
import type { Metadata } from "next";
import localFont from "next/font/local";
import "./globals.css";
import theme from "./themes/defaultTheme";
import {ThemeProvider} from "@mui/material"

const geistSans = localFont({
  src: "./fonts/GeistVF.woff",
  variable: "--font-geist-sans",
  weight: "100 900",
});
const geistMono = localFont({
  src: "./fonts/GeistMonoVF.woff",
  variable: "--font-geist-mono",
  weight: "100 900",
});

// export const metadata: Metadata = {
//   title: "Create Next App",
//   description: "Generated by create next app",
// };

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body
        className={`${geistSans.variable} ${geistMono.variable} antialiased`}
      >
        <ThemeProvider theme={theme}>
          main1
          {children}
        </ThemeProvider>
      </body>
    </html>
  );
}

Not Found & Error Boundary Page

  • Error page must be client component

app/not-found.tsx
import Link from 'next/link'
 
export default function NotFound() {
  return (
    <div>
      <h2>Not Found</h2>
      <p>Could not find requested resource</p>
      <Link href="/">Return Home</Link>
    </div>
  )
}
'use client' // Error boundaries must be Client Components
 
import { useEffect } from 'react'
 
export default function Error({
  error,
  reset,
}: {
  error: Error & { digest?: string }
  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>
  )
}

Route Folder & File naming

  • (Folder name) can be used without affecting the path

  • page.tsx refer to the page

  • [slug] refer to dynamic page

[slug]/page.tsx
export default async function Page({
    params,
  }: {
    params: Promise<{ slug: string }>
  }) { 
    const slug = (await params).slug;

    return (
      <div>My Post: {slug}</div>
    )
  }

Loading

  • The data fetching support Suspense of react

page.tsx
import { Suspense } from "react";
import axios from "axios";
export default async function Page({
    params,
  }: {
    params: Promise<{ slug: string }>
  }) {

    async function getMovies() {
      const {data} = await axios.get(
        `https://fakestoreapi.com/products`
      );
      // await new Promise((resolve) => setTimeout(resolve, 2000));
      return data;
    }

    const results = await getMovies();
    console.log(results);
  
    const slug = (await params).slug;

    return (
    <Suspense>
      <div>My Post: {slug}</div>
    </Suspense>
    )
  }
loading.tsx
import React from 'react'

const Loading = () => {
  return (
    <div>Loading</div>
  )
}

export default Loading

Parallel Route

  • Parallel Routes allows you to simultaneously or conditionally render one or more pages within the same layout.

  • Each route can actually be treated as component on layout

  • Each route nested route pattern must be the same

@user/page.tsx
import React from 'react'

const UserPage = () => {
  return (
    <div>User Page</div>
  )
}

export default UserPage
page.tsx
import React from 'react'
import TestCompoent from './components/test'
import Link from 'next/link'

const Main = () => {
  return (
    <div>
      Main Page
    </div>
  )
}

export default Main
layout.tsx
"use client"

import React from "react"

export default function Layout({
  user,
  children, // will be a page or nested layout
  }: {
    children: React.ReactNode
    user: React.ReactNode
  }) {
    return (
      <>
        /* User */
        {user}
        /* Main */
        {children}
      </>
    )
  }
PreviousPage RouterNextReact Native

Last updated 5 months ago

Was this helpful?

Configure how the request should interact with Next.js .

Data Cache
The pre-rendered html page
The hydrated html page
Result of fetching RSC payload
Result
After disabling