> For the complete documentation index, see [llms.txt](https://tech.x2bee.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://tech.x2bee.com/dev-guide/developer-guide-en/pjt-prepare/publish-your-docs/store-front-framework-next.js/02.-coding-guide-and-essential-packages/7.-other-loading-suspense-zod-etc..md).

# 7. Other (loading, Suspense, zod, etc.)

This document provides example code for using loading, error boundaries, zod, dynamic routes, and searchParams in a Next.js project.

***

## 1. Dynamic Routes

Dynamic Routes are conventionally used with a name like \[slug] when you want many product names to share the same layout. You don't necessarily have to use the name slug; below, for readability, the example uses the name `categoryname`.

Example with the following folder structure

```
└── category
    └── [categoryname]
        └── page.tsx
```

page.tsx

{% code title="app/category/\[categoryname]/page.tsx" %}

```tsx
import React from "react";

const Page = ({ params }: { params: { categoryname: string } }) => {
  return (
    <div>
      <h1>{params.categoryname}</h1>
    </div>
  );
};

export default Page;
```

{% endcode %}

If you enter <http://localhost:3000/category/dress> in your browser, `dress` will be rendered.

When using react-router-dom with React, Nested Routing configuration can make the code longer, but in Next.js, as shown above, you can receive `params` directly, which is concise.

***

## 2. searchParams

`searchParams` is used to pass search information via the URI when searching for a product.

Server Component (page) example:

{% code title="app/somepage/page.tsx" %}

```tsx
const Page = ({
  searchParams,
}: {
  searchParams: { id: string | undefined };
}) => {
  return (
    <div>
      <h1>{searchParams.id}</h1>
    </div>
  );
};

export default Page;
```

{% endcode %}

Implementing the same feature in a Client Component:

{% code title="app/somepage/client-page.tsx" %}

```tsx
"use client";
import { useSearchParams } from "next/navigation";

const Page = () => {
  const searchParams = useSearchParams();
  const id = searchParams.get("id");

  return (
    <div>
      <h1>{id}</h1>
    </div>
  );
};

export default Page;
```

{% endcode %}

***

## 3. Example Using zod, loading, error boundary, dynamic routes

Example folder structure:

```
├── events
│   ├── [id]
│   │   ├── loading.tsx
│   │   └── page.tsx
│   └── error.tsx
```

Install zod:

```
pnpm add zod
```

The example below is for when accessing `http://localhost:8077/events/seoul?sp1=test&pageno=2`

* params = { id: 'seoul' }
* searchParams = { sp1: 'test', pageno: '2' }

page.tsx (Server Component)

{% code title="app/events/\[id]/page.tsx" %}

```tsx
import { Suspense } from 'react';
import Loading from './loading';
import { z } from 'zod';

interface Props {
  params: { id: string };
  searchParams: { [key: string]: string | string[] | undefined };
}

// searchParam 값은 string이므로 숫자로 바꾸고, 정수이면서 양수인지를 체크
const pageNumberSchema = z.coerce.number().int().positive();

const Page = ({ params, searchParams }: Props) => {
  console.log('search', searchParams);

  const parsedPageNo = pageNumberSchema.safeParse(searchParams.pageno);
  console.log('parsedPageNo', parsedPageNo);
  // pageno값이 '2'이면 결과 : parsedPageNo { success: true, data: 2 }
  // pageno값이 '0'이면 결과 : parsedPageNo { success: false, error: [Getter] }

  if (!parsedPageNo.success) {
    // 즉, 양수가 아니면 에러를 던진다.
    throw new Error('Invalid page number');
  }

  // error.tsx는 같은 폴더에 있어도 되지만 없다면 상위 폴더의 error.tsx가 실행된다.
  return (
    <main className="py-24 text-center">
      <Suspense key={parsedPageNo.data} fallback={<Loading />}>
        <p>page number is {parsedPageNo.data}</p>
      </Suspense>
    </main>
  );
};

export default Page;
```

{% endcode %}

In loading.tsx, you can register a spinner animation or icon, and you can also register a `<Skeleton />` using a UI library.

loading.tsx example (simple)

{% code title="app/events/\[id]/loading.tsx" %}

```tsx
export default function Loading() {
  return (
    <div className="py-24 text-center">
      <p>Loading...</p>
    </div>
  );
}
```

{% endcode %}

error.tsx must always work only in a Client Component.

error.tsx (client only)

{% code title="app/events/error.tsx" %}

```tsx
'use client'; // Error components 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 (
    <main className="py-24 text-center">
      <p>{error.message}</p>
      <button
        className="mt-4 border bg-blue-500 px-4 py-2 text-white"
        onClick={
          // Attempt to recover by trying to re-render the segment
          reset
        }
      >
        Try again
      </button>
    </main>
  );
}
```

{% endcode %}

If there is no `error.tsx`, and there is no error boundary at a higher level, the entire app can crash. Therefore, it's a good idea to place an error component in each segment (folder) to prevent the entire app from crashing, and to provide the user with a means of recovery, such as retrying.

<figure><img src="https://200425-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FVEZx3rsZsIv89GPS3d2J%2Fuploads%2FAqmFJpWTtmKu8m8j53zM%2Fimg%20(21).png?alt=media&#x26;token=45dd323e-dfc2-4742-9aac-d1b952b71a77" alt=""><figcaption></figcaption></figure>

<div align="left"><figure><img src="https://200425-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FVEZx3rsZsIv89GPS3d2J%2Fuploads%2F78ZH7oJ6aIdH6SjsMkBQ%2Fimg%20(22).png?alt=media&#x26;token=37144072-1d45-49db-925d-ff5607fb0a56" alt=""><figcaption></figcaption></figure></div>

In the case above, validation was done via searchParams, but the same approach can also be applied using react-query or fetch.
