From 1d37713b57a0781d8e62f08c84e208c41a9cfdf1 Mon Sep 17 00:00:00 2001 From: Lee Robinson Date: Thu, 3 Feb 2022 12:35:22 -0600 Subject: [PATCH] Update custom document docs to prepare for React 18. (#33814) To help prepare for React 18, I've found myself sharing the updated code snippet of `_document` linked in the [React 18 docs](https://nextjs.org/docs/advanced-features/react-18#react-server-components), which uses a function instead of a class. This PR updates the Custom Document docs to help prepare for this change, by encourage readers to use the new version, and listing more tangible examples of why you would override `_document` (`lang` on `html`, className on `body`). It also reorganizes some of the caveats and warnings related to `getInitialProps` usage to only be in the single section that applies to it. These overrides and ejections (`getInitialProps` and `renderPage`) only apply to a subset of the people looking to change document. Co-authored-by: JJ Kasper <22380829+ijjk@users.noreply.github.com> --- docs/advanced-features/custom-document.md | 83 ++++++++++++----------- 1 file changed, 44 insertions(+), 39 deletions(-) diff --git a/docs/advanced-features/custom-document.md b/docs/advanced-features/custom-document.md index b6af8d8b18b1e..843f5db272c87 100644 --- a/docs/advanced-features/custom-document.md +++ b/docs/advanced-features/custom-document.md @@ -4,76 +4,67 @@ description: Extend the default document markup added by Next.js. # Custom `Document` -A custom `Document` is commonly used to augment your application's `` and `` tags. This is necessary because Next.js pages skip the definition of the surrounding document's markup. +A custom `Document` can update the `` and `` tags used to render a [Page](/docs/basic-features/pages.md). This file is only rendered on the server, so event handlers like `onClick` cannot be used in `_document`. -To override the default `Document`, create the file `./pages/_document.js` and extend the `Document` class as shown below: +To override the default `Document`, create the file `pages/_document.js` as shown below: ```jsx -import Document, { Html, Head, Main, NextScript } from 'next/document' - -class MyDocument extends Document { - static async getInitialProps(ctx) { - const initialProps = await Document.getInitialProps(ctx) - return { ...initialProps } - } - - render() { - return ( - - - -
- - - - ) - } +import { Html, Head, Main, NextScript } from 'next/document' + +export default function Document() { + return ( + + + +
+ + + + ) } - -export default MyDocument ``` -> The code above is the default `Document` added by Next.js. Feel free to remove the `getInitialProps` or `render` function from `MyDocument` if you don't need to change them. - -``, ``, `
` and `` are required for the page to be properly rendered. - -Custom attributes are allowed as props, like `lang`: +The code above is the default `Document` added by Next.js. Custom attributes are allowed as props. For example, we might want to add `lang="en"` to the `` tag: ```jsx ``` -The `` component used here is not the same one from [`next/head`](/docs/api-reference/next/head.md). The `` component used here should only be used for any `` code that is common for all pages. For all other cases, such as `` tags, we recommend using [`next/head`](/docs/api-reference/next/head.md) in your pages or components. +Or add a `className` to the `body` tag: -The `ctx` object is equivalent to the one received in [`getInitialProps`](/docs/api-reference/data-fetching/get-initial-props.md#context-object), with one addition: +```jsx +<body className="bg-white"> +``` -- `renderPage`: `Function` - a callback that runs the actual React rendering logic (synchronously). It's useful to decorate this function in order to support server-rendering wrappers like Aphrodite's [`renderStatic`](https://github.com/Khan/aphrodite#server-side-rendering) +`<Html>`, `<Head />`, `<Main />` and `<NextScript />` are required for the page to be properly rendered. ## Caveats -- `Document` is only rendered in the server, event handlers like `onClick` won't work. -- React components outside of `<Main />` will not be initialized by the browser. Do _not_ add application logic here or custom CSS (like `styled-jsx`). If you need shared components in all your pages (like a menu or a toolbar), take a look at the [`App`](/docs/advanced-features/custom-app.md) component instead. -- `Document`'s `getInitialProps` function is not called during client-side transitions, nor when a page is [statically optimized](/docs/advanced-features/automatic-static-optimization.md). +- The `<Head />` component used in `_document` is not the same as [`next/head`](/docs/api-reference/next/head.md). The `<Head />` component used here should only be used for any `<head>` code that is common for all pages. For all other cases, such as `<title>` tags, we recommend using [`next/head`](/docs/api-reference/next/head.md) in your pages or components. +- React components outside of `<Main />` will not be initialized by the browser. Do _not_ add application logic here or custom CSS (like `styled-jsx`). If you need shared components in all your pages (like a menu or a toolbar), read [Layouts](/docs/basic-features/layouts.md) intead. - `Document` currently does not support Next.js [Data Fetching methods](/docs/basic-features/data-fetching/overview.md) like [`getStaticProps`](/docs/basic-features/data-fetching/get-static-props.md) or [`getServerSideProps`](/docs/basic-features/data-fetching/get-server-side-props.md). ## Customizing `renderPage` -> It should be noted that the only reason you should be customizing `renderPage` is for usage with **css-in-js** libraries that need to wrap the application to properly work with server-side rendering. +> **Note:** This is advanced and only needed for libraries like CSS-in-JS to support server-side rendering. This is not needed for built-in `styled-jsx` support. -It takes as argument an options object for further customization: +To prepare for [React 18](/docs/advanced-features/react-18.md), we recommend avoiding customizing `getInitiaProps` and `renderPage`, if possible. + +The `ctx` object shown below is equivalent to the one received in [`getInitialProps`](/docs/api-reference/data-fetching/get-initial-props.md#context-object), with the addition of `renderPage`. ```jsx -import Document from 'next/document' +import Document, { Html, Head, Main, NextScript } from 'next/document' class MyDocument extends Document { static async getInitialProps(ctx) { const originalRenderPage = ctx.renderPage + // Run the React rendering logic synchronously ctx.renderPage = () => originalRenderPage({ - // useful for wrapping the whole react tree + // Useful for wrapping the whole react tree enhanceApp: (App) => App, - // useful for wrapping in a per-page basis + // Useful for wrapping in a per-page basis enhanceComponent: (Component) => Component, }) @@ -82,11 +73,25 @@ class MyDocument extends Document { return initialProps } + + render() { + return ( + <Html> + <Head /> + <body> + <Main /> + <NextScript /> + </body> + </Html> + ) + } } export default MyDocument ``` +> **Note**: `getInitialProps` in `_document` is not called during client-side transitions. + ## TypeScript You can use the built-in `DocumentContext` type and change the file name to `./pages/_document.tsx` like so: