mirror of
https://github.com/hanzoai/next.js.git
synced 2026-10-30 14:46:38 +00:00
<!-- Thanks for opening a PR! Your contribution is much appreciated. To make sure your PR is handled as smoothly as possible we request that you follow the checklist sections below. Choose the right checklist for the change(s) that you're making: ## For Contributors ### Improving Documentation - Run `pnpm prettier-fix` to fix formatting issues before opening the PR. - Read the Docs Contribution Guide to ensure your contribution follows the docs guidelines: https://nextjs.org/docs/community/contribution-guide ### Fixing a bug - Related issues linked using `fixes #number` - Tests added. See: https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs - Errors have a helpful link attached, see https://github.com/vercel/next.js/blob/canary/contributing.md ### Adding a feature - Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. (A discussion must be opened, see https://github.com/vercel/next.js/discussions/new?category=ideas) - Related issues/discussions are linked using `fixes #number` - e2e tests added (https://github.com/vercel/next.js/blob/canary/contributing/core/testing.md#writing-tests-for-nextjs) - Documentation added - Telemetry added. In case of a feature if it's used or not. - Errors have a helpful link attached, see https://github.com/vercel/next.js/blob/canary/contributing.md ## For Maintainers - Minimal description (aim for explaining to someone not on the team to understand the PR) - When linking to a Slack thread, you might want to share details of the conclusion - Link both the Linear (Fixes NEXT-xxx) and the GitHub issues - Add review comments if necessary to explain to the reviewer the logic behind a change ### What? This PR updates the documentation to clarify the interaction between `loading.js` and `layout.js`. ### Why? To address a common point of confusion and potential blocking navigation issues when layouts are dynamic. It provides clear guidance on how `loading.js` behaves relative to layouts and how to use `<Suspense>` for dynamic layout parts. ### How? - Added a "Good to know" callout in `loading.mdx` explaining that `loading.js` does not wrap the layout. - Introduced a new "Interaction with `loading.js`" section under Caveats in `layout.mdx`, including an explanation and a code example for wrapping dynamic layout components with `<Suspense>`. - Added a brief "Good to know" callout in `fetching-data.mdx` for consistency. Closes NEXT- Fixes # --> --- [Slack Thread](https://vercel.slack.com/archives/C05KYT5S9FF/p1772239839900129?thread_ts=1772239839.900129&cid=C05KYT5S9FF) <p><a href="https://cursor.com/agents/bc-d69fb736-2a00-5f68-9e28-9b31c194f12a"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-web-light.png"><img alt="Open in Web" width="114" height="28" src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a> <a href="https://cursor.com/background-agent?bcId=bc-d69fb736-2a00-5f68-9e28-9b31c194f12a"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img alt="Open in Cursor" width="131" height="28" src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a> </p> --------- Co-authored-by: Cursor Agent <[email protected]> Co-authored-by: Joseph Chamochumbi <[email protected]> Co-authored-by: Delba de Oliveira <[email protected]>
241 lines
8.3 KiB
Plaintext
241 lines
8.3 KiB
Plaintext
---
|
||
title: page.js
|
||
description: API reference for the page.js file.
|
||
---
|
||
|
||
The `page` file allows you to define UI that is **unique** to a route. You can create a page by default exporting a component from the file:
|
||
|
||
```tsx filename="app/blog/[slug]/page.tsx" switcher
|
||
export default function Page({
|
||
params,
|
||
searchParams,
|
||
}: {
|
||
params: Promise<{ slug: string }>
|
||
searchParams: Promise<{ [key: string]: string | string[] | undefined }>
|
||
}) {
|
||
return <h1>My Page</h1>
|
||
}
|
||
```
|
||
|
||
```jsx filename="app/blog/[slug]/page.js" switcher
|
||
export default function Page({ params, searchParams }) {
|
||
return <h1>My Page</h1>
|
||
}
|
||
```
|
||
|
||
## Good to know
|
||
|
||
- The `.js`, `.jsx`, or `.tsx` file extensions can be used for `page`.
|
||
- A `page` is always the **leaf** of the route subtree.
|
||
- A `page` file is required to make a route segment **publicly accessible**.
|
||
- Pages are [Server Components](https://react.dev/reference/rsc/server-components) by default, but can be set to a [Client Component](https://react.dev/reference/rsc/use-client).
|
||
- In the [component hierarchy](/docs/app/getting-started/project-structure#component-hierarchy), `page.js` is the innermost file convention. It is wrapped by `loading.js` (Suspense boundary), `error.js` (error boundary), `template.js`, and `layout.js` in the same segment.
|
||
|
||
## Reference
|
||
|
||
### Props
|
||
|
||
#### `params` (optional)
|
||
|
||
A promise that resolves to an object containing the [dynamic route parameters](/docs/app/api-reference/file-conventions/dynamic-routes) from the root segment down to that page.
|
||
|
||
```tsx filename="app/shop/[slug]/page.tsx" switcher
|
||
export default async function Page({
|
||
params,
|
||
}: {
|
||
params: Promise<{ slug: string }>
|
||
}) {
|
||
const { slug } = await params
|
||
}
|
||
```
|
||
|
||
```jsx filename="app/shop/[slug]/page.js" switcher
|
||
export default async function Page({ params }) {
|
||
const { slug } = await params
|
||
}
|
||
```
|
||
|
||
| Example Route | URL | `params` |
|
||
| ------------------------------------ | ----------- | --------------------------------------- |
|
||
| `app/shop/[slug]/page.js` | `/shop/1` | `Promise<{ slug: '1' }>` |
|
||
| `app/shop/[category]/[item]/page.js` | `/shop/1/2` | `Promise<{ category: '1', item: '2' }>` |
|
||
| `app/shop/[...slug]/page.js` | `/shop/1/2` | `Promise<{ slug: ['1', '2'] }>` |
|
||
|
||
- Since the `params` prop is a promise, you must use `async/await` or React's [`use`](https://react.dev/reference/react/use) function to access the values.
|
||
- In version 14 and earlier, `params` was a synchronous prop. To help with backwards compatibility, you can still access it synchronously in Next.js 15, but this behavior will be deprecated in the future.
|
||
|
||
#### `searchParams` (optional)
|
||
|
||
A promise that resolves to an object containing the [search parameters](https://developer.mozilla.org/docs/Learn/Common_questions/What_is_a_URL#parameters) of the current URL. For example:
|
||
|
||
```tsx filename="app/shop/page.tsx" switcher
|
||
export default async function Page({
|
||
searchParams,
|
||
}: {
|
||
searchParams: Promise<{ [key: string]: string | string[] | undefined }>
|
||
}) {
|
||
const filters = (await searchParams).filters
|
||
}
|
||
```
|
||
|
||
```jsx filename="app/shop/page.js" switcher
|
||
export default async function Page({ searchParams }) {
|
||
const filters = (await searchParams).filters
|
||
}
|
||
```
|
||
|
||
Client Component **pages** can also access `searchParams` using React’s [`use`](https://react.dev/reference/react/use) hook:
|
||
|
||
```tsx filename="app/shop/page.tsx" switcher
|
||
'use client'
|
||
import { use } from 'react'
|
||
|
||
export default function Page({
|
||
searchParams,
|
||
}: {
|
||
searchParams: Promise<{ [key: string]: string | string[] | undefined }>
|
||
}) {
|
||
const filters = use(searchParams).filters
|
||
}
|
||
```
|
||
|
||
```jsx filename="app/page.jsx" switcher
|
||
'use client'
|
||
import { use } from 'react'
|
||
|
||
export default function Page({ searchParams }) {
|
||
const filters = use(searchParams).filters
|
||
}
|
||
```
|
||
|
||
| Example URL | `searchParams` |
|
||
| --------------- | ----------------------------- |
|
||
| `/shop?a=1` | `Promise<{ a: '1' }>` |
|
||
| `/shop?a=1&b=2` | `Promise<{ a: '1', b: '2' }>` |
|
||
| `/shop?a=1&a=2` | `Promise<{ a: ['1', '2'] }>` |
|
||
|
||
- Since the `searchParams` prop is a promise. You must use `async/await` or React's [`use`](https://react.dev/reference/react/use) function to access the values.
|
||
- In version 14 and earlier, `searchParams` was a synchronous prop. To help with backwards compatibility, you can still access it synchronously in Next.js 15, but this behavior will be deprecated in the future.
|
||
- `searchParams` is a **[Request-time API](/docs/app/glossary#request-time-apis)** whose values cannot be known ahead of time. Using it will opt the page into **[dynamic rendering](/docs/app/glossary#dynamic-rendering)** at request time.
|
||
- `searchParams` is a plain JavaScript object, not a `URLSearchParams` instance.
|
||
|
||
### Page Props Helper
|
||
|
||
You can type pages with `PageProps` to get strongly typed `params` and `searchParams` from the route literal. `PageProps` is a globally available helper.
|
||
|
||
```tsx filename="app/blog/[slug]/page.tsx"
|
||
export default async function Page(props: PageProps<'/blog/[slug]'>) {
|
||
const { slug } = await props.params
|
||
const query = await props.searchParams
|
||
return <h1>Blog Post: {slug}</h1>
|
||
}
|
||
```
|
||
|
||
> **Good to know**
|
||
>
|
||
> - Using a literal route (e.g. `'/blog/[slug]'`) enables autocomplete and strict keys for `params`.
|
||
> - Static routes resolve `params` to `{}`.
|
||
> - Types are generated during `next dev`, `next build`, or with `next typegen`.
|
||
> - After type generation, the `PageProps` helper is globally available. It doesn't need to be imported.
|
||
|
||
## Examples
|
||
|
||
### Displaying content based on `params`
|
||
|
||
Using [dynamic route segments](/docs/app/api-reference/file-conventions/dynamic-routes), you can display or fetch specific content for the page based on the `params` prop.
|
||
|
||
```tsx filename="app/blog/[slug]/page.tsx" switcher
|
||
export default async function Page({
|
||
params,
|
||
}: {
|
||
params: Promise<{ slug: string }>
|
||
}) {
|
||
const { slug } = await params
|
||
return <h1>Blog Post: {slug}</h1>
|
||
}
|
||
```
|
||
|
||
```jsx filename="app/blog/[slug]/page.js" switcher
|
||
export default async function Page({ params }) {
|
||
const { slug } = await params
|
||
return <h1>Blog Post: {slug}</h1>
|
||
}
|
||
```
|
||
|
||
### Handling filtering with `searchParams`
|
||
|
||
You can use the `searchParams` prop to handle filtering, pagination, or sorting based on the query string of the URL.
|
||
|
||
```tsx filename="app/shop/page.tsx" switcher
|
||
export default async function Page({
|
||
searchParams,
|
||
}: {
|
||
searchParams: Promise<{ [key: string]: string | string[] | undefined }>
|
||
}) {
|
||
const { page = '1', sort = 'asc', query = '' } = await searchParams
|
||
|
||
return (
|
||
<div>
|
||
<h1>Product Listing</h1>
|
||
<p>Search query: {query}</p>
|
||
<p>Current page: {page}</p>
|
||
<p>Sort order: {sort}</p>
|
||
</div>
|
||
)
|
||
}
|
||
```
|
||
|
||
```jsx filename="app/shop/page.js" switcher
|
||
export default async function Page({ searchParams }) {
|
||
const { page = '1', sort = 'asc', query = '' } = await searchParams
|
||
|
||
return (
|
||
<div>
|
||
<h1>Product Listing</h1>
|
||
<p>Search query: {query}</p>
|
||
<p>Current page: {page}</p>
|
||
<p>Sort order: {sort}</p>
|
||
</div>
|
||
)
|
||
}
|
||
```
|
||
|
||
### Reading `searchParams` and `params` in Client Components
|
||
|
||
To use `searchParams` and `params` in a Client Component (which cannot be `async`), you can use React's [`use`](https://react.dev/reference/react/use) function to read the promise:
|
||
|
||
```tsx filename="app/page.tsx" switcher
|
||
'use client'
|
||
|
||
import { use } from 'react'
|
||
|
||
export default function Page({
|
||
params,
|
||
searchParams,
|
||
}: {
|
||
params: Promise<{ slug: string }>
|
||
searchParams: Promise<{ [key: string]: string | string[] | undefined }>
|
||
}) {
|
||
const { slug } = use(params)
|
||
const { query } = use(searchParams)
|
||
}
|
||
```
|
||
|
||
```js filename="app/page.js" switcher
|
||
'use client'
|
||
|
||
import { use } from 'react'
|
||
|
||
export default function Page({ params, searchParams }) {
|
||
const { slug } = use(params)
|
||
const { query } = use(searchParams)
|
||
}
|
||
```
|
||
|
||
## Version History
|
||
|
||
| Version | Changes |
|
||
| ------------ | ---------------------------------------------------------------------------------------------------------------- |
|
||
| `v15.0.0-RC` | `params` and `searchParams` are now promises. A [codemod](/docs/app/guides/upgrading/codemods#150) is available. |
|
||
| `v13.0.0` | `page` introduced. |
|