# 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

{% code title="layout.tsx" %}
```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>
  );
}
```
{% endcode %}

```tsx
import ClientComponent from "./components/ClientComponent";
import ServerComponent from "./components/ServerComponent";

export default function Home() {
  return (
    <>
      <ClientComponent/>
      <ServerComponent/>
    </>
  );
}
```

{% code title="ClientComponent.tsx" %}
```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;
```
{% endcode %}

{% code title="ServerCompoenent.tsx" %}
```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;
```
{% endcode %}

### Result

<figure><img src="https://1374779285-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MFW3x2aqEO8GF2kr3VU%2Fuploads%2F24NZDrbWcGUaqou6iaUV%2Fimage.png?alt=media&#x26;token=a7b864df-1a4f-415c-a2d5-7eee94222c13" alt=""><figcaption><p>The pre-rendered html page</p></figcaption></figure>

<figure><img src="https://1374779285-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MFW3x2aqEO8GF2kr3VU%2Fuploads%2FosMLukfGXxiMkrV7Iqb6%2Fimage.png?alt=media&#x26;token=0f5f9f8e-8b08-490d-88a1-0ef9764e79ae" alt=""><figcaption><p>The hydrated html page</p></figcaption></figure>

## Link Navigation

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

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

export default OtherPage
```

<figure><img src="https://1374779285-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MFW3x2aqEO8GF2kr3VU%2Fuploads%2FdwpiToP4CRMBls6r5Bxt%2Fimage.png?alt=media&#x26;token=9a2f2d11-7d78-4f68-ab0f-2c3b6355d0e8" alt=""><figcaption><p>Result of fetching RSC payload</p></figcaption></figure>

* 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,&#x20;
* 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&#x20;

## 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`

{% code title="api/route.ts" %}
```typescript
import { NextResponse } from 'next/server';

export function GET() {
  const randomNumber = Math.floor(Math.random() * 1000);
  const message = { message: `${randomNumber}` };
  return NextResponse.json(message);   
}
```
{% endcode %}

```tsx
// 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

<figure><img src="https://1374779285-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MFW3x2aqEO8GF2kr3VU%2Fuploads%2FHzMsjMwVO5a5iRqM1mLm%2Fimage.png?alt=media&#x26;token=07bc684f-9736-43c9-9851-b8004bdec8cc" alt=""><figcaption></figcaption></figure>

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

### Request memoization

<figure><img src="https://1374779285-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MFW3x2aqEO8GF2kr3VU%2Fuploads%2FpvBG70AKY01FvwVvyx9L%2Fimage.png?alt=media&#x26;token=a8a1073c-1f87-489b-b150-d6e970984837" alt=""><figcaption></figcaption></figure>

* 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.

```tsx
import Link from "next/link";
import ServerComponent from "./components/ServerComponent";
export default function Home() {
  
  return (
    <>
      <ServerComponent/>
      <ServerComponent/>
      <Link href="/other">Go to other</Link>
    </>
  );
}
```

```tsx
// 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;
```

<figure><img src="https://1374779285-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MFW3x2aqEO8GF2kr3VU%2Fuploads%2Fvya1UivDVuzwGaJmwbja%2Fimage.png?alt=media&#x26;token=f1779cdd-a1e3-41b7-9109-b0ad8d25556e" alt=""><figcaption><p>Result</p></figcaption></figure>

* To disable request memoization, we need integrate with `AbortController`

```tsx
// 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;
```

<figure><img src="https://1374779285-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MFW3x2aqEO8GF2kr3VU%2Fuploads%2FS0I29aOBAbdlnOjqq1tS%2Fimage.png?alt=media&#x26;token=10eca6aa-dc42-4a0d-83ef-e16742e1a50b" alt=""><figcaption><p>After disabling</p></figcaption></figure>

### 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 <a href="#optionscache" id="optionscache"></a>

* Configure how the request should interact with Next.js [Data Cache](https://nextjs.org/docs/app/building-your-application/caching#data-cache).

```
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)

<figure><img src="https://1374779285-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MFW3x2aqEO8GF2kr3VU%2Fuploads%2FvsBbU630kTTNKfdtqild%2Fimage.png?alt=media&#x26;token=e953cb9a-7a66-4de6-9030-3cfeb064b33c" alt=""><figcaption></figcaption></figure>

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

```tsx
// 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&#x20;

{% code title="app/layout.tsx" %}
```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>
  );
}
```
{% endcode %}

## Not Found & Error Boundary Page

* Error page must be client component

{% code title="app/not-found.tsx" %}
```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>
  )
}
```
{% endcode %}

```tsx
'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&#x20;

* `(Folder name)` can be used without affecting the path
* `page.tsx` refer to the page
* `[slug]` refer to dynamic page

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

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

## Loading&#x20;

* The data fetching support `Suspense` of react&#x20;

{% code title="page.tsx" %}
```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>
    )
  }
```
{% endcode %}

{% code title="loading.tsx" %}
```tsx
import React from 'react'

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

export default Loading
```
{% endcode %}

## Parallel Route

* Parallel Routes allows you to simultaneously or conditionally render one or more pages within the same layout.&#x20;
* Each route can actually be treated as component on layout
* Each route nested route pattern must be the same

{% code title="@user/page.tsx" %}
```tsx
import React from 'react'

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

export default UserPage
```
{% endcode %}

{% code title="page.tsx" %}
```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
```
{% endcode %}

{% code title="layout.tsx" %}
```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}
      </>
    )
  }
```
{% endcode %}
