ben-rogerson / twin.macro

🦹‍♂️ Twin blends the magic of Tailwind with the flexibility of css-in-js (emotion, styled-components, solid-styled-components, stitches and goober) at build time.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

`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

  1. Open this reproduction or clone this repo.
  2. Install dependencies, if you cloned the repo.
  3. 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 :)