diff --git a/next-env.d.ts b/next-env.d.ts index 52e831b4342..3cd7048ed94 100644 --- a/next-env.d.ts +++ b/next-env.d.ts @@ -1,5 +1,6 @@ /// /// +/// // NOTE: This file should not be edited -// see https://nextjs.org/docs/pages/api-reference/config/typescript for more information. +// see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/src/content/reference/react-dom/server/index.md b/src/content/reference/react-dom/server/index.md index bea11603de3..c3138767cf2 100644 --- a/src/content/reference/react-dom/server/index.md +++ b/src/content/reference/react-dom/server/index.md @@ -10,19 +10,20 @@ The `react-dom/server` APIs let you server-side render React components to HTML. --- -## Server APIs for Node.js Streams {/*server-apis-for-nodejs-streams*/} - -These methods are only available in the environments with [Node.js Streams:](https://nodejs.org/api/stream.html) +## Server APIs for Web Streams {/*server-apis-for-web-streams*/} -* [`renderToPipeableStream`](/reference/react-dom/server/renderToPipeableStream) renders a React tree to a pipeable [Node.js Stream.](https://nodejs.org/api/stream.html) +These methods are only available in the environments with [Web Streams](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API), which includes browsers, Deno, and some modern edge runtimes: +* [`renderToReadableStream`](/reference/react-dom/server/renderToReadableStream) renders a React tree to a [Readable Web Stream.](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) +* [`resume`](/reference/react-dom/server/renderToPipeableStream) resumes [`prerender`](/reference/react-dom/static/prerender) to a [Readable Web Stream](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream). --- -## Server APIs for Web Streams {/*server-apis-for-web-streams*/} +## Server APIs for Node.js Streams {/*server-apis-for-nodejs-streams*/} -These methods are only available in the environments with [Web Streams](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API), which includes browsers, Deno, and some modern edge runtimes: +These methods are only available in the environments with [Node.js Streams:](https://nodejs.org/api/stream.html) -* [`renderToReadableStream`](/reference/react-dom/server/renderToReadableStream) renders a React tree to a [Readable Web Stream.](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) +* [`renderToPipeableStream`](/reference/react-dom/server/renderToPipeableStream) renders a React tree to a pipeable [Node.js Stream.](https://nodejs.org/api/stream.html) +* [`resumeToPipeableStream`](/reference/react-dom/server/renderToPipeableStream) resumes [`prerenderToNodeStream`](/reference/react-dom/static/prerenderToNodeStream) to a pipeable [Node.js Stream.](https://nodejs.org/api/stream.html) --- diff --git a/src/content/reference/react-dom/server/resume.md b/src/content/reference/react-dom/server/resume.md new file mode 100644 index 00000000000..b0367391f4c --- /dev/null +++ b/src/content/reference/react-dom/server/resume.md @@ -0,0 +1,240 @@ +--- +title: resume +canary: true +--- + + + +`resume` streams a pre-rendered React tree to a [Readable Web Stream.](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) + +```js +const stream = await resume(reactNode, postponedState, options?) +``` + + + + + + + +This API depends on [Web Streams.](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API) For Node.js, use [`resumeToNodeStream`](/reference/react-dom/server/renderToPipeableStream) instead. + + + +--- + +## Reference {/*reference*/} + +### `resume(node, postponed, options?)` {/*resume*/} + +Call `resume` to resume rendering a pre-rendered React tree as HTML into a [Readable Web Stream.](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) + +```js +import { resume } from 'react-dom/server'; +import {getPostponedState} from 'storage'; + +async function handler(request) { + const postponed = await getPostponedState(request); + const stream = await resume(, postponed, { + bootstrapScripts: ['/main.js'] + }); + return new Response(stream, { + headers: { 'content-type': 'text/html' }, + }); +} +``` + +TODO: when do you call hydrateRoot? In the shell or when you resume? + +[See more examples below.](#usage) + +#### Parameters {/*parameters*/} + +* `reactNode`: The React node you called `prerender` with. For example, a JSX element like ``. It is expected to represent the entire document, so the `App` component should render the `` tag. +* `postponedState`: The opaque `postpone` object returned from `prerender`, loaded from wherever you stored it (e.g. redis, a file, or S3). +* **optional** `options`: An object with streaming options. + * **optional** `nonce`: A [`nonce`](http://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#nonce) string to allow scripts for [`script-src` Content-Security-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src). + * **optional** `signal`: An [abort signal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) that lets you [abort server rendering](#aborting-server-rendering) and render the rest on the client. + * **optional** `onError`: A callback that fires whenever there is a server error, whether [recoverable](#recovering-from-errors-outside-the-shell) or [not.](#recovering-from-errors-inside-the-shell) By default, this only calls `console.error`. If you override it to [log crash reports,](#logging-crashes-on-the-server) make sure that you still call `console.error`. + + +#### Returns {/*returns*/} + +`resume` returns a Promise: + +- If `prerender` successfully produced a [shell](#specifying-what-goes-into-the-shell) is successful, that Promise will resolve to a [Readable Web Stream.](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) whether replaying the shell errors or not. +- If `prerender` failed to produce a [shell](#specifying-what-goes-into-the-shell), and `resume` errors, the Promise will be rejected. TODO: Example? + +The returned stream has an additional property: + +* `allReady`: A Promise that resolves when all rendering is complete. You can `await stream.allReady` before returning a response [for crawlers and static generation.](#waiting-for-all-content-to-load-for-crawlers-and-static-generation) If you do that, you won't get any progressive loading. The stream will contain the final HTML. + +#### Caveats {/*caveats*/} +- `resume` does not accept options for `bootstrapScripts`, `bootstrapScriptContent`, or `bootstrapModules`. Instead, you need to pass these options to the `prerender` call that generates the `postponedState`. These may be injected either during pre-render or resume. +- `resume` does not accept `identifierPrefix` since the prefix needs to be the same in both `prerender` and `resume`. +- Since `nonce` cannot be provided to prerender, you should only provide `nonce` to `resume` if you're not providing scripts to prerender. +- `resume` re-renders from the root until it finds a component that was not fully pre-rendered, and skips fully pre-rendered components. +--- + +## Usage {/*usage*/} + +### Resuming a prerender to a Readable Web Stream {/*resuming-a-prerender-to-a-readable-web-stream*/} + +TODO + +--- + +### Logging crashes on the server {/*logging-crashes-on-the-server*/} + +By default, all errors on the server are logged to console. You can override this behavior to log crash reports: + +```js {9-10} +import { resume } from 'react-dom/server'; +import { getPostponedState } from 'storage'; +import { logServerCrashReport } from 'logging'; + +async function handler(request) { + const postponed = await getPostponedState(request); + const stream = await resume(, postponed, { + onError(error) { + console.error(error); + logServerCrashReport(error); + } + }); + return new Response(stream, { + headers: { 'content-type': 'text/html' }, + }); +} +``` + +If you provide a custom `onError` implementation, don't forget to also log errors to the console like above. + +--- + +### Recovering from errors replaying the shell {/*recovering-from-errors-inside-the-shell*/} + +TODO: this is for when the shell completed. + +In this example, prerender successfully rendered a shell containing `ProfileLayout`, `ProfileCover`, and `PostsGlimmer`: + +```js {3-5,7-8} +function ProfilePage() { + return ( + + + }> + + + + ); +} +``` + +If an error occurs while replaying those components, React won't have any meaningful HTML to send to the client. TODO: how to recover from this, since the promise is resolved. I think it will just encode an error in the stream and trigger an error boundary? + +```js {2,13-18} +// TODO +``` + +If there is an error while replaying the shell, it will be logged to `onError`. + +### Recovering from errors re-creating the shell {/*recovering-from-errors-re-creating-the-shell*/} + +TODO: this is for when the shell errors, and re-creating the shell fails. + +--- + +### Recovering from errors outside the shell {/*recovering-from-errors-outside-the-shell*/} + +TODO: confirm this section is correct. + +In this example, the `` component is wrapped in `` so it is *not* a part of the shell: + +```js {6} +function ProfilePage() { + return ( + + + }> + + + + ); +} +``` + +If an error happens in the `Posts` component or somewhere inside it, React will [try to recover from it:](/reference/react/Suspense#providing-a-fallback-for-server-errors-and-client-only-content) + +1. It will emit the loading fallback for the closest `` boundary (`PostsGlimmer`) into the HTML. +2. It will "give up" on trying to render the `Posts` content on the server anymore. +3. When the JavaScript code loads on the client, React will *retry* rendering `Posts` on the client. + +If retrying rendering `Posts` on the client *also* fails, React will throw the error on the client. As with all the errors thrown during rendering, the [closest parent error boundary](/reference/react/Component#static-getderivedstatefromerror) determines how to present the error to the user. In practice, this means that the user will see a loading indicator until it is certain that the error is not recoverable. + +If retrying rendering `Posts` on the client succeeds, the loading fallback from the server will be replaced with the client rendering output. The user will not know that there was a server error. However, the server `onError` callback and the client [`onRecoverableError`](/reference/react-dom/client/hydrateRoot#hydrateroot) callbacks will fire so that you can get notified about the error. + +--- + +### Setting the status code {/*setting-the-status-code*/} + +TODO: you can't set the status code in resume, unless you're calling prerender in the same request. If so, set the status code between `prerender` and `resume`. + +--- + +### Handling different errors in different ways {/*handling-different-errors-in-different-ways*/} + +TODO: update this example. + +You can [create your own `Error` subclasses](https://javascript.info/custom-errors) and use the [`instanceof`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof) operator to check which error is thrown. For example, you can define a custom `NotFoundError` and throw it from your component. Then you can save the error in `onError` and do something different before returning the response depending on the error type: + +```js {2-3,5-15,22,28,33} +async function handler(request) { + let didError = false; + let caughtError = null; + + function getStatusCode() { + if (didError) { + if (caughtError instanceof NotFoundError) { + return 404; + } else { + return 500; + } + } else { + return 200; + } + } + + try { + const stream = await renderToReadableStream(, { + bootstrapScripts: ['/main.js'], + onError(error) { + didError = true; + caughtError = error; + console.error(error); + logServerCrashReport(error); + } + }); + return new Response(stream, { + status: getStatusCode(), + headers: { 'content-type': 'text/html' }, + }); + } catch (error) { + return new Response('

Something went wrong

', { + status: getStatusCode(), + headers: { 'content-type': 'text/html' }, + }); + } +} +``` + +--- + +### Waiting for all content to load for crawlers and static generation {/*waiting-for-all-content-to-load-for-crawlers-and-static-generation*/} + +TODO: this doesn't make sense for `resume` right? + +--- + +### Aborting server rendering {/*aborting-server-rendering*/} + +TODO \ No newline at end of file diff --git a/src/content/reference/react-dom/server/resumeToPipeableStream.md b/src/content/reference/react-dom/server/resumeToPipeableStream.md new file mode 100644 index 00000000000..13dae562118 --- /dev/null +++ b/src/content/reference/react-dom/server/resumeToPipeableStream.md @@ -0,0 +1,245 @@ +--- +title: resumeToPipeableStream +canary: true +--- + + + +`resume` streams a pre-rendered React tree to a pipeable [Node.js Stream.](https://nodejs.org/api/stream.html) + +```js +const {pipe, abort} = await resumeToPipeableStream(reactNode, postponedState, options?) +``` + + + + + + + +This API is specific to Node.js. Environments with [Web Streams,](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API) like Deno and modern edge runtimes, should use [`resume`](/reference/react-dom/server/renderToReadableStream) instead. + + + +--- + +## Reference {/*reference*/} + +### `resumeToPipeableStream(node, postponed, options?)` {/*resume-to-pipeable-stream*/} + +Call `resume` to resume rendering a pre-rendered React tree as HTML into a [Node.js Stream.](https://nodejs.org/api/stream.html#writable-streams) + +```js +import { resume } from 'react-dom/server'; +import {getPostponedState} from 'storage'; + +async function handler(request) { + const postponed = await getPostponedState(request); + const {pipe} = await resumeToPipeableStream(, postponed, { + onShellReady: () => { + pipe(response); + } + }); +} +``` + +TODO: when do you call hydrateRoot? In the shell or when you resume? + +[See more examples below.](#usage) + +#### Parameters {/*parameters*/} + +* `reactNode`: The React node you called `prerender` with. For example, a JSX element like ``. It is expected to represent the entire document, so the `App` component should render the `` tag. +* `postponedState`: The opaque `postpone` object returned from `prerender`, loaded from wherever you stored it (e.g. redis, a file, or S3). +* **optional** `options`: An object with streaming options. + * **optional** `nonce`: A [`nonce`](http://developer.mozilla.org/en-US/docs/Web/HTML/Element/script#nonce) string to allow scripts for [`script-src` Content-Security-Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src). + * **optional** `onAllReady`: A callback that fires when all rendering is complete. You can use this instead of `onShellReady` [for crawlers and static generation.](#waiting-for-all-content-to-load-for-crawlers-and-static-generation) If you start streaming here, you won't get any progressive loading. The stream will contain the final HTML. + * **optional** `onError`: A callback that fires whenever there is a server error, whether [recoverable](#recovering-from-errors-outside-the-shell) or [not.](#recovering-from-errors-inside-the-shell) By default, this only calls `console.error`. If you override it to [log crash reports,](#logging-crashes-on-the-server) make sure that you still call `console.error`. + * **optional** `onShellReady`: A callback that fires right after the [initial shell](#specifying-what-goes-into-the-shell) has been replayed. You can call `pipe` here to start streaming. React will [stream the additional content](#streaming-more-content-as-it-loads) after the shell along with the inline `