Next.js and Styled Components go out of sync between the server and the client on refresh
Asked Answered
C

3

7

I have a Next.js app using styled components. On first load of any page, there are no complaints, and everything looks properly styled. When I refresh a page however, everything still looks proper, but I get a console error reading:

Warning: Prop `className` did not match. Server: "sc-TXQaF bfnBGK" Client: "sc-bdnylx kKokSB"

I've tried simplifying the styles on the specific component, and the error persists. I've tried removing the component entirely from the DOM, and that results in the same error on the next element in the DOM. So it seems to be a global issue.

I've followed the guide for using Next.js and Styled Components found here: https://github.com/vercel/next.js/tree/master/examples/with-styled-components

I have the .babelrc file in the root:

{
  "presets": ["next/babel"],
  "plugins": [["styled-components", { "ssr": true }]]
}

I have the _document.js file in my pages directory:

import Document from 'next/document'
import { ServerStyleSheet } from 'styled-components'

export default class MyDocument extends Document {
    static async getInitialProps(ctx) {
        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()
        }
    }
}

Here is an example of one of my styled components:

import styled from 'styled-components';

export const Block = styled.div`
margin: ${props => props.small ? '2rem 0' : '4rem 0'};
margin-top: ${props => props.clearTop ? '0' : null};
`;

... although I've tried to dumb it down to something as simple as this with no change in the console error:

import styled from 'styled-components';

export const Block = styled.div`
position: relative;
`;

Finally, here's a dumbed down page that still produces the error:

import { useContext, useEffect } from 'react';
import { useRouter } from 'next/router';

import Layout from '../components/layout';
import { Block } from '../components/styled/Block';

import { userContext } from '../context/userContext';;

function Profile() {

  const router = useRouter();

  const { loggedIn } = useContext(userContext);

  useEffect(() => {
    if (!loggedIn) router.push('/login');
  }, [loggedIn]);

  return (
    <Layout>
      <Block>
        <h1>Test</h1>
      </Block>
    </Layout>
  )

}

export default Profile;

Kind of at my wits end here.

Cooperman answered 8/11, 2020 at 16:49 Comment(0)
C
6

I believe I figured out an answer. I didn't have the dev dependency for babel styled components.

npm install babel-plugin-styled-components --save-dev

Your package.json file should have this:

"devDependencies": {
  "babel-plugin-styled-components": "^1.11.1"
}

After this was installed, along with the _document.js and .babelrc files correctly placed in your app, you shouldn't have any problems.

Cooperman answered 8/11, 2020 at 19:22 Comment(1)
it did the trick for me. It is quite cumbersome all the configs you have to do in order to make styled components work properly in next.js though.Rriocard
M
4

I had this issue for the last 1 month and finally got a solution that worked for me!

So the solution here is to get the styling exclusively from the server.

from the docs:

Basically you need to add a custom pages/_document.js (if you don't have one). Then copy the logic for styled-components to inject the server side rendered styles into the <head>

To solve this issue is you need something like this in your Document component:

import Document from 'next/document'
import { ServerStyleSheet } from 'styled-components'

export default class MyDocument extends Document {
    static async getInitialProps(ctx) {

    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()
    }
  }
}

The final step (if the error persists) is to delete the cache: delete the .next folder and restart the server

The full example code from Next documentation is Here

Madden answered 10/9, 2021 at 12:3 Comment(0)
V
0

with the Next Complier on the latest version of next, you should only update your next.config file and _document file, and you will be all set. Babel will cause conflict with the NextJS compiler.

Here you can check the files. If you don't use TS, just replace

ctx: DocumentContext

with only

ctx

Example app with styled-components

V1 answered 21/11, 2022 at 19:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.