Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/01-app/01-getting-started/06-fetching-data.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ There are two ways you can use streaming in your application:
1. Wrapping a page with a [`loading.js` file](#with-loadingjs)
2. Wrapping a component with [`<Suspense>`](#with-suspense)

> **Good to know:** Bots and crawlers are served differently from browsers. Next.js waits for data fetching to finish and sends the fully rendered page instead of streaming it progressively. See [Bots and crawlers](/docs/app/guides/streaming#bots-and-crawlers).

#### With `loading.js`

You can create a `loading.js` file in the same folder as your page to stream the **entire page** while the data is being fetched. For example, to stream `app/blog/page.js`, add the file inside the `app/blog` folder.
Expand Down
6 changes: 6 additions & 0 deletions docs/01-app/01-getting-started/08-caching.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,12 @@ Next.js requires you to explicitly handle components that can't complete during

> **🎥 Watch:** Why Partial Prerendering and how it works → [YouTube (10 minutes)](https://www.youtube.com/watch?v=MTcPrTIBkpA).

### Bots and crawlers

Browsers receive the static shell instantly. Bots and crawlers are detected by their user agent and handled differently: because they need a complete document, Next.js skips the shell and renders the entire page dynamically at request time, then sends the finished HTML once the render completes.

Because the shell is re-rendered instead of reused, work that completed during prerendering now runs at request time for a bot. If part of your shell depends on inputs that only exist while prerendering, such as build-time data or values that are not reachable in the request-time environment, a page that loads for a person can fail to render for a crawler. Make sure the data your shell relies on is also available at request time. See [Bots and crawlers](/docs/app/guides/streaming#bots-and-crawlers) for more details.

### Opting out of the static shell

Placing a `<Suspense>` boundary with an empty fallback above the document body in your Root Layout causes the entire app to defer to request time. Because the fallback is empty, there is no static shell to send immediately, so every request blocks until the page is fully rendered. To limit this to specific routes, use [multiple root layouts](/docs/app/api-reference/file-conventions/layout#root-layout).
Expand Down
20 changes: 16 additions & 4 deletions docs/01-app/02-guides/streaming.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -666,13 +666,17 @@ export default async function PostPage({ params }) {

> **Good to know:** You can also reject requests early using [`proxy`](/docs/app/api-reference/file-conventions/proxy) (for redirects, rewrites, or returning a response) or [`next.config.js` redirects](/docs/app/api-reference/config/next-config-js/redirects). Both run before the page renders, so HTTP status codes are still available.

### Metadata and bots
### Bots and crawlers

[`generateMetadata`](/docs/app/api-reference/functions/generate-metadata) resolves before streaming begins for bots that only scrape static HTML (such as Twitterbot or Slackbot). For full browsers and capable crawlers, metadata can [stream](/docs/app/api-reference/functions/generate-metadata#streaming-metadata) alongside the page content.
Bots and crawlers consume a complete, fully formed HTML document rather than rendering chunks as they arrive. Next.js detects them by their user agent and waits for the render to finish, then sends the whole document in a single response instead of streaming it progressively. [`generateMetadata`](/docs/app/api-reference/functions/generate-metadata) resolves before the response is sent for bots that only scrape static HTML (such as Twitterbot or Slackbot), so metadata is present in the `<head>` of that HTML. Full browsers and capable crawlers can instead receive [streaming metadata](/docs/app/api-reference/functions/generate-metadata#streaming-metadata) alongside the page content.

Next.js automatically detects user agents to choose the right behavior. You can customize which bots receive blocking metadata with the [`htmlLimitedBots`](/docs/app/api-reference/config/next-config-js/htmlLimitedBots) configuration option.
You can customize which bots receive blocking metadata with the [`htmlLimitedBots`](/docs/app/api-reference/config/next-config-js/htmlLimitedBots) configuration option. See the [`loading.js` SEO section](/docs/app/api-reference/file-conventions/loading#seo) for more details.

See the [`loading.js` SEO section](/docs/app/api-reference/file-conventions/loading#seo) for more details.
#### Cache Components

With [Cache Components](/docs/app/getting-started/caching), a visitor receives the prerendered shell immediately and dynamic content streams in as it resolves. A bot is served differently: because it needs a complete document, Next.js skips the prerendered shell and renders the entire page dynamically at request time, waiting for the full render to complete before sending the finished HTML.

Keep this in mind when your prerendered shell depends on inputs that only exist while prerendering, such as build-time data or values that are not reachable in the request-time environment. A visitor receives the shell without re-running that code, but a bot re-renders it dynamically, so a page that loads for a person can fail to render for a crawler. Make sure any data the shell relies on is also available at request time.

## What can affect streaming

Expand Down Expand Up @@ -762,6 +766,14 @@ The `<template id="B:0">` markers are the Suspense fallback placeholders. When a

> **Good to know:** The `Accept-Encoding: identity` header disables compression so chunks are not buffered by the compression layer.

**Compare a bot request.** Add a bot user agent to the same script with `headers: { 'User-Agent': 'Twitterbot/1.0', 'Accept-Encoding': 'identity' }`. Now `await fetch()` itself blocks until the full render completes (around 3 seconds for this page), because the server holds the response until it has the finished document. The body then arrives all at once, with none of the staggered `+1000ms` / `+3000ms` timestamps:

```text filename="Terminal"
chunk 0 (+0ms) # Entire document in a single burst, after fetch() already waited
```

This is the [bots and crawlers](#bots-and-crawlers) behavior: the server waits for the full render and sends one fully formed HTML document instead of streaming.

### Platform support

| Deployment Option | Supported |
Expand Down
Loading