`className` mismatch between server and client while using emotion
kaguya3222 opened this issue · comments
What is happening
I use Next.js + twin.macro + emotion. If I refresh the page I get an error about client and server mismatch of className
attribute.
How to reproduce
- Open this reproduction or clone this repo.
- Install dependencies, if you cloned the repo.
- Reload the index page and see warning in console about
className
mismatch.
What is expected
No warnings or errors in console
Error (example)
next-dev.js?3515:20 Warning: Prop `className` did not match. Server: "css-1b7g2n7-Container-Container-Container e1mc56xh0" Client: "css-1p0a8i8-Container-Container-Container-Container e1mc56xh0"
at div
at eval (webpack-internal:///./node_modules/@emotion/react/dist/emotion-element-6a883da9.browser.esm.js:57:66)
at div
at Home
at MyApp (webpack-internal:///./pages/_app.tsx:27:25)
at ErrorBoundary (webpack-internal:///./node_modules/next/dist/compiled/@next/react-dev-overlay/dist/client.js:8:20742)
at ReactDevOverlay (webpack-internal:///./node_modules/next/dist/compiled/@next/react-dev-overlay/dist/client.js:8:23635)
at Container (webpack-internal:///./node_modules/next/dist/client/index.js:111:5)
at AppContainer (webpack-internal:///./node_modules/next/dist/client/index.js:296:24)
at Root (webpack-internal:///./node_modules/next/dist/client/index.js:500:25)
See more info here: https://nextjs.org/docs/messages/react-hydration-error
Additional context
The code that produces this issue is located in pages/index.tsx, 3rd line. When I use emotion styled components + emotion css (that are imported from twin.macro) + props, then I get this error.
Example:
import { styled, css } from "twin.macro";
const Container = styled("div")(({ color }) => [
css`
color: ${color};
`,
]);
This issue can be reproduced in twin.examples: https://github.com/ben-rogerson/twin.examples/tree/master/next-emotion-typescript
If I change import to this (code below) then everything works without warnings.
import styled from "@emotion/styled";
import { css } from "@emotion/react";
Also, if I remove props from styled component, then it also works without warnings:
import { styled, css } from "twin.macro";
const Container = styled("div")(() => [
css`
color: red;
`,
]);
Thanks for the great issue writeup 👍
I've taken a look into this and haven't been able to work out a fix - it's quite strange.
The only thing I can think of - is that the reference to css
is lost during the conversion.
Here's the only conversion that takes place with your code above:
import { css as _css } from "@emotion/react";
import _styled from "@emotion/styled";
const Container = _styled("div")(({
color
}) => [_css`
color: ${color};
`]);
Here's some workarounds I've found:
// Use css object styles
const Container = styled("div")(({ color }) => [css({ color: color })]);
// Or move `css` out of `styled`
const makeColor = (color) => css`
color: ${color};
`;
const Container = styled("div")(({ color }) => [makeColor(color)]);
@ben-rogerson Thank you for the response! Comparing the workarounds I think the best is to explicitly import styled
and css
from @emotion
packages, not from twin.macro
.
import styled from "@emotion/styled";
import { css } from "@emotion/react";
What do you think ?
I think it's okey because twin.macro does this under the hood and also it's more explicit way for new developers to say what css-in-js we use
Yeah you can avoid the twin imports altogether - can't think of any side-effects - you'll just have more imports per file.
Worth asking - which issues made removing css
from inside styled
a no-go 🙅 ?
const Container = styled("div")(() => [
css` // < Without css?
color: red;
`,
]);
@ben-rogerson I think there will be no issues, but I've understood that for me and my team it's better to use only css
, without styled
, so we'll go this way :)