豆豆友情提示:这是一个非官方 GitHub 代理镜像,主要用于网络测试或访问加速。请勿在此进行登录、注册或处理任何敏感信息。进行这些操作请务必访问官方网站 github.com。 Raw 内容也通过此代理提供。
Skip to content

Commit db7a645

Browse files
committed
Polish export fallback docs
1 parent a0d1610 commit db7a645

File tree

3 files changed

+66
-15
lines changed

3 files changed

+66
-15
lines changed

docs/01-app/02-guides/static-exports.mdx

Lines changed: 48 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -165,19 +165,20 @@ module.exports = nextConfig
165165
During `next build`, Next.js uses [vary params](/docs/app/api-reference/functions/generate-static-params) to determine which parts of each route depend on which dynamic params. It then renders a fallback shell per dynamic route:
166166

167167
- **Param-independent layouts and components** (those that don't read the dynamic param) render fully as Server Components at build time.
168-
- **Param-dependent components** (those that `await params`) suspend at the nearest [Suspense boundary](/docs/app/guides/streaming#granular-streaming-with-suspense), showing your `loading.tsx` or `<Suspense fallback>` UI.
168+
- **Param-dependent boundaries** suspend at the nearest [Suspense boundary](/docs/app/guides/streaming#granular-streaming-with-suspense), showing your `loading.tsx` or `<Suspense fallback>` UI.
169169

170-
Wrap param-dependent content in a Suspense boundary so the fallback shell can show meaningful loading UI:
170+
For `output: 'export'`, the most ergonomic shape is a shared Server Component shell with a Client Component leaf for paths you did not prerender with `generateStaticParams`. Wrap that client leaf in a Suspense boundary so the fallback shell can show meaningful loading UI:
171171

172172
```tsx filename="app/blog/[slug]/page.tsx" switcher
173173
import { Suspense } from 'react'
174+
import { BlogPostClient } from './blog-post-client'
174175

175-
export default function Page({ params }: PageProps<'/blog/[slug]'>) {
176+
export default function Page() {
176177
return (
177178
<main>
178179
<Sidebar /> {/* Renders fully in the shell (doesn't read slug) */}
179180
<Suspense fallback={<PostSkeleton />}>
180-
<BlogPost params={params} /> {/* Shows skeleton until slug resolves */}
181+
<BlogPostClient />
181182
</Suspense>
182183
</main>
183184
)
@@ -186,22 +187,49 @@ export default function Page({ params }: PageProps<'/blog/[slug]'>) {
186187

187188
```jsx filename="app/blog/[slug]/page.js" switcher
188189
import { Suspense } from 'react'
190+
import { BlogPostClient } from './blog-post-client'
189191

190-
export default function Page({ params }) {
192+
export default function Page() {
191193
return (
192194
<main>
193195
<Sidebar /> {/* Renders fully in the shell (doesn't read slug) */}
194196
<Suspense fallback={<PostSkeleton />}>
195-
<BlogPost params={params} /> {/* Shows skeleton until slug resolves */}
197+
<BlogPostClient />
196198
</Suspense>
197199
</main>
198200
)
199201
}
200202
```
201203

204+
```tsx filename="app/blog/[slug]/blog-post-client.tsx" switcher
205+
'use client'
206+
207+
import { useParams } from 'next/navigation'
208+
209+
export function BlogPostClient() {
210+
const { slug } = useParams<{ slug: string }>()
211+
212+
return <Post slug={slug} />
213+
}
214+
```
215+
216+
```jsx filename="app/blog/[slug]/blog-post-client.js" switcher
217+
'use client'
218+
219+
import { useParams } from 'next/navigation'
220+
221+
export function BlogPostClient() {
222+
const { slug } = useParams()
223+
224+
return <Post slug={slug} />
225+
}
226+
```
227+
228+
> **Good to know**: A Suspense boundary lets Next.js emit the fallback shell at build time, but it does not create a runtime server request later. For unknown params in `output: 'export'`, request-specific data must still cross into a Client Component.
229+
202230
#### Combining with `generateStaticParams`
203231

204-
If you export `generateStaticParams`, the returned params are fully prerendered at build time. Any other param values fall back to the shared shell. This lets you prerender your most popular pages while still supporting the long tail:
232+
If you export `generateStaticParams`, the returned params are fully prerendered at build time, which lets you keep using Server Components at build time for those known paths. Any other param values fall back to the shared shell. In other words, the same route can use both fully prerendered params and fallback-rendered unknown params:
205233

206234
```tsx filename="app/blog/[slug]/page.tsx"
207235
// These slugs get full static HTML at build time
@@ -222,12 +250,12 @@ For example, `/docs/[section]/[page]` is matched before `/docs/[...slug]`, so `/
222250

223251
To serve unenumerated params on a static host, configure the host's 404 or error document to serve `_fallback.html` from the export output for document navigations:
224252

225-
| Host | Configuration |
226-
| --------------- | ---------------------------------------------------- |
227-
| Nginx | `try_files $uri $uri/ /_fallback.html` |
228-
| Apache | `ErrorDocument 404 /_fallback.html` |
229-
| S3 + CloudFront | Custom error response: 404 → `/_fallback.html` (200) |
230-
| GitHub Pages | Rename `_fallback.html` to `404.html` |
253+
| Host | Configuration |
254+
| --------------- | -------------------------------------------------- |
255+
| Nginx | `try_files $uri $uri.html $uri/ /_fallback.html` |
256+
| Apache | `ErrorDocument 404 /_fallback.html` |
257+
| S3 + CloudFront | Custom error response: 403/404 → `/_fallback.html` |
258+
| GitHub Pages | Copy `_fallback.html` to `404.html` |
231259

232260
When a user visits an unenumerated route, the host serves `_fallback.html`. The Next.js client router then discovers the correct fallback shell and renders the page with param-dependent content loading on the client.
233261

@@ -272,6 +300,12 @@ server {
272300

273301
With `trailingSlash: true`, the same config works because prerendered routes are emitted as `/route/index.html` and unknown paths still fall back to `/_fallback.html`. With `trailingSlash: false`, keep the `try_files $uri $uri.html $uri/ /_fallback.html;` order so prerendered flat files are served before the fallback bootstrap.
274302

303+
#### Deployment tips
304+
305+
- **S3 + CloudFront**: If your S3 origin returns `403` for missing files, configure CloudFront custom error responses for both `403` and `404` to serve `/_fallback.html`. Use a `200` response code if you want SPA-style hard navigations for unknown params.
306+
- **GitHub Pages**: Keep `_fallback.html` as the canonical export artifact, then copy it to `404.html` as part of your deploy step. GitHub Pages uses `404.html` as its static fallback entrypoint.
307+
- **First paint**: `/_fallback.html` is a bootstrap document, not the final page. The first meaningful UI should come from the resolved fallback route shell, so prefer neutral loading states over route-specific content in that phase.
308+
275309
#### Server and client boundaries
276310

277311
Fallback routes in `output: 'export'` do not have a runtime server request. For unenumerated paths, request-specific APIs and unresolved fallback params are only available on the client.
@@ -301,6 +335,7 @@ export default async function Page() {
301335
> **Good to know**:
302336
>
303337
> - Param-dependent content for fallback routes renders on the client, so use client-side fetching patterns like `fetch`, SWR, or React Query in those parts of the tree.
338+
> - Prefer `generateStaticParams` for SEO-critical or frequently visited routes, then let other paths use the export fallback shell.
304339
> - Direct requests for unenumerated params may initially return a 404 status from the static host before the fallback shell loads. SEO-sensitive pages should be listed in `generateStaticParams`.
305340
> - This mode does not provide ISR. Rebuilding is still required to promote a fallback path to a fully prerendered page.
306341
> - Route names starting with `__fallback` are reserved and cannot be used with this feature.

docs/01-app/03-api-reference/03-file-conventions/dynamic-routes.mdx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,8 @@ All params are runtime data. Param access must be wrapped by Suspense fallback U
160160

161161
> **Good to know**: You can also use [`loading.tsx`](/docs/app/api-reference/file-conventions/loading) for page-level fallback UI.
162162
163+
> **Good to know**: When using [`output: 'export'`](/docs/app/guides/static-exports), the component that consumes unknown params should typically be a Client Component. That keeps the shared shell static while the long-tail route resolves on the client.
164+
163165
```tsx filename="app/blog/[slug]/page.tsx"
164166
import { Suspense } from 'react'
165167

@@ -291,7 +293,15 @@ async function PrivatePost({ slug }: { slug: string }) {
291293
}
292294
```
293295

294-
When using [`output: 'export'`](/docs/app/guides/static-exports) for App Router dynamic routes with `cacheComponents: true`, `generateStaticParams` is optional. Next.js emits a shared fallback shell for the dynamic route, prerenders any param-independent layouts or Server Components at build time, and resolves param-dependent content on the client. If you still export `generateStaticParams`, those params are fully prerendered and the rest use the fallback shell.
296+
When using [`output: 'export'`](/docs/app/guides/static-exports) for App Router dynamic routes with `cacheComponents: true`, `generateStaticParams` is optional. Next.js emits a shared fallback shell for the dynamic route, prerenders any param-independent layouts or Server Components at build time, and resolves param-dependent content on the client. If you still export `generateStaticParams`, those params are fully prerendered so you can keep using Server Components at build time for the paths you know ahead of time, and the rest use the fallback shell. The same route can do both at once: prerender the params you return and fall back for the params you do not return.
297+
298+
For the best ergonomics in `output: 'export'`:
299+
300+
- Keep shared layout and shell content in Server Components.
301+
- Move param reads and request-specific data for paths you did not prerender into Client Components.
302+
- Use Suspense to show loading UI while the client resolves those paths.
303+
304+
> **Good to know**: In `output: 'export'`, a Suspense boundary makes the shell prerenderable, but it does not create a runtime server request. Unknown params still need a Client Component boundary if they depend on request-time data.
295305
296306
## Examples
297307

docs/01-app/03-api-reference/04-functions/generate-static-params.mdx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -311,10 +311,16 @@ When using [Cache Components](/docs/app/getting-started/caching) with dynamic ro
311311

312312
For server deployments, `generateStaticParams` must return **at least one param**. Empty arrays cause a [build error](/docs/messages/empty-generate-static-params). This allows Cache Components to validate your route doesn't incorrectly access `cookies()`, `headers()`, or `searchParams` at runtime.
313313

314-
When using [`output: 'export'`](/docs/app/guides/static-exports) with App Router dynamic routes, `generateStaticParams` becomes optional when `cacheComponents: true` is enabled. If you omit it, Next.js emits a fallback shell for the route and resolves param-dependent content on the client. If you provide it, the returned params are prerendered and all other params use the fallback shell.
314+
When using [`output: 'export'`](/docs/app/guides/static-exports) with App Router dynamic routes, `generateStaticParams` becomes optional when `cacheComponents: true` is enabled. If you omit it, Next.js emits a fallback shell for the route and resolves param-dependent content on the client. If you provide it, the returned params are prerendered so you can keep using Server Components at build time for the paths you know ahead of time, and all other params use the fallback shell. The same route can mix both behaviors: prerendered params from `generateStaticParams` and fallback rendering for params you did not return.
315315

316316
When multiple dynamic route patterns share the same static prefix in `output: 'export'`, the fallback shell still follows normal App Router route specificity. For example, if both `/docs/[section]/[page]` and `/docs/[...slug]` exist, params returned by `generateStaticParams` for the specific route are prerendered as usual, while unknown params still fall back and match the most specific route first.
317317

318+
For the best authoring ergonomics in `output: 'export'`:
319+
320+
- Return the params you care about most for SEO or fast first loads.
321+
- Let other paths use the export fallback shell.
322+
- Keep param-dependent request data in Client Components for paths you did not prerender.
323+
318324
> **Good to know**: If you don't know the actual param values at build time, you can return a placeholder param (e.g., `[{ slug: '__placeholder__' }]`) for validation, then handle it in your page with `notFound()`. However, this prevents build time validation from working effectively and may cause runtime errors.
319325
320326
See the [dynamic routes section](/docs/app/api-reference/file-conventions/dynamic-routes#with-cache-components) for detailed walkthroughs.

0 commit comments

Comments
 (0)