Search code examples
next.jsstyled-components

how do I merge NextJS current _document.js with <Head> with export default class code from Next/StyledComponents example?


Following the instructions here: https://medium.com/nerd-for-tech/using-next-js-with-styled-components-easy-dfff3849e4f1 to configure nextJS for styled-components and got stuck trying to merge current _document.js

export default function Document() {
  return (
    <Html>
      <Head>
        <link href="https://fonts.googleapis.com" rel="preconnect" />
        <link crossOrigin href="https://fonts.gstatic.com" rel="preconnect" />
        <link
          href="https://fonts.googleapis.com/css2?family=Merriweather&family=Newsreader:opsz,wght@6..72,400;6..72,500&family=Work+Sans:wght@700&display=swap"
          rel="stylesheet"
        />
      </Head>
      <body>
        <Main />
        <NextScript />
      </body>
    </Html>
  ); 

with the

export default class MyDocument extends Document {
  static async getInitialProps(
    ctx: DocumentContext
  ): Promise<DocumentInitialProps> {
    const sheet = new ServerStyleSheet()
    const originalRenderPage = ctx.renderPage

    try {
      ctx.renderPage = () =>
        originalRenderPage({
          enhanceApp: (App) => (props) =>
            sheet.collectStyles(<App {...props} />),
        })

      const initialProps = await Document.getInitialProps(ctx)
      return {
        ...initialProps,
        styles: [
          <>
            {initialProps.styles}
            {sheet.getStyleElement()}
          </>,
        ],
      }
    } finally {
      sheet.seal()
    }
  }
}

What should the combined code look like? Thanks.

Solution

  • In your case the export default function Document() is a function that corresponds to the "render" part of a class Component.

    And in the class component that you pasted, the render method is not overwritten.

    This means the easiest way for you is to move everything from your original function to the render method in the class component.

    This is how it would look like:

    export default class MyDocument extends Document {
      static async getInitialProps(
        ctx: DocumentContext
      ): Promise<DocumentInitialProps> {
        const sheet = new ServerStyleSheet();
        const originalRenderPage = ctx.renderPage;
    
        try {
          ctx.renderPage = () =>
            originalRenderPage({
              enhanceApp: (App) => (props) =>
                sheet.collectStyles(<App {...props} />),
            });
    
          const initialProps = await Document.getInitialProps(ctx);
          return {
            ...initialProps,
            styles: [
              <>
                {initialProps.styles}
                {sheet.getStyleElement()}
              </>,
            ],
          };
        } finally {
          sheet.seal();
        }
      }
    
      render() {
        return (
          <Html>
            <Head>
              <link href="https://fonts.googleapis.com" rel="preconnect" />
              <link crossOrigin href="https://fonts.gstatic.com" rel="preconnect" />
              <link
                href="https://fonts.googleapis.com/css2?family=Merriweather&family=Newsreader:opsz,wght@6..72,400;6..72,500&family=Work+Sans:wght@700&display=swap"
                rel="stylesheet"
              />
            </Head>
            <body>
              <Main />
              <NextScript />
            </body>
          </Html>
        );
      }
    }
    

    Alternatively, you could do the other way round and add getInitialProps to your function component.

    In order for you to understand what's going on here, you could look up the differences between class components and function components in React. Or read this lengthy blog post: https://overreacted.io/how-are-function-components-different-from-classes/