emotion-js / emotion

πŸ‘©β€πŸŽ€ CSS-in-JS library designed for high performance style composition
https://emotion.sh/
MIT License
17.48k stars 1.11k forks source link

Nextjs Prop Classname Does Not Match Error #1984

Closed johnstonbl01 closed 4 years ago

johnstonbl01 commented 4 years ago

Current behavior:

Hey! πŸ‘‹

I'm running into an issue using Next's SSG in conjunction with Emotion's CSS prop & object styling where creating conditional styles is causing layout issues and the dreaded Prop className did not match error.

Here's a video of the issue happening: https://v.usetapes.com/wCGkiL4Q1q

In the code, I'm creating a style based on the random generation of a number. When Next compiles the code, if the applied style is different from what the client tries to render, the error appears. I originally ran into this issue trying to use a similar approach for different device sizes and prop values for a component.

The example code:

import * as styles from '../shared/styles';

const Home = () => {
  const randomNbr = Math.random() * 10;
  const conditional = randomNbr > 5 ? { color: 'darkorchid' } : null;

  return (
    <div>
      <div css={[styles.basic, conditional]}>Cool Styles</div>
      <div css={[styles.basic, styles.hover, styles.code]}>
        With <code>:hover</code>.
      </div>
      <div css={[styles.basic, styles.hover, styles.code, styles.animation]}>
        Let's bounce
      </div>
    </div>
  );
};

export default Home;

To reproduce:

Repo with reproduction: https://github.com/johnstonbl01/emotion-nextjs-issue-example

This is a copy of the Nextjs example app, but just changed it to use the css prop instead of styled components.

  1. Install dependencies (npm install)
  2. Run the project (npm run dev)
  3. If the error does not appear right away in the console, refresh the page a couple times and it should show up

Expected behavior:

The conditional style should be applied on the client-side without showing an error.

Environment information:

(I have also seen this with @latest versions of both libraries)

Andarist commented 4 years ago

Well, you can't have random styles and expect for SSRed content to always match the client-generated one. It's just not possible - the problem is not specific to Emotion, but to any rendered output based on random values. You could run into this with React alone, without any dependencies or anything.

If you really need something like this then I would recommend randomizing on the server BUT providing the randomized values as static global values that could be reused by the client to generate the same content as the server.

johnstonbl01 commented 4 years ago

@Andarist Thanks. The randomized example is just for the sake of an easy explanation.

More commonly where I have run into this is where SSG renders a desktop view and I want to do something like:

const Home = () => {
  const conditional = device.isMobile && { someStyle: 'value' };

  return (
    <div>
      <div css={[styles.basic, conditional]}>Cool Styles</div>
      <div css={[styles.basic, styles.hover, styles.code]}>
        With <code>:hover</code>.
      </div>
      <div css={[styles.basic, styles.hover, styles.code, styles.animation]}>
        Let's bounce
      </div>
    </div>
  );
};

export default Home;

In the above cases, I end up getting the same issue. Is that also something that isn't possible / shouldn't be done?

Andarist commented 4 years ago

In the above cases, I end up getting the same issue. Is that also something that isn't possible / shouldn't be done?

Well, yeah - the problem is the very same one as with a condition based on a random value. Unless you are able to SSR per request and you can reliably provide the same value to device.isMobile on both the server and on the client.

You should either use media queries for this or defer rendering of device-specific content so it wouldn't be rendered on the server.

johnstonbl01 commented 4 years ago

Makes perfect sense. I'm glad that's the issue and not that I was just configuring something wrong. πŸ˜‚ Thanks for your time!