It's fine locally (known warning and CSS renders well), but on Vercel my Remix app gets this error:
Hydration failed because the initial UI does not match what was rendered on the server.
Business logic runs fine but CSS is utterly broken.
Update 26 June 2022, 15:50
I started a new project from scratch and added dependencies one by one and deploying to Vercel at each step. No errors. Styled components render well. So the dependencies are not the problem.
I then started fetching data piece by piece from my database through the loader and rendering them in styled components one by one. The one thing that consistently breaks the CSS and produces the error is the conversion of a datetime object to a string before rendering:
const DateTimeSpan = styled.span`
font-size: 1rem;
`;
const hr = now.getHours();
const min = now.getMinutes();
<DateTimeSpan>
{`${hr}:${min}`}
</DateTimeSpan>
Curiously, it's only when I format it to render only time that it breaks. With date, it's fine:
const yr = now.getFullYear();
const mth = now.getMonth();
const dd = now.getDate();
<DateTimeSpan>
{`${yr}-${mth}-${dd}`}
<DateTimeSpan>
I'm at a loss to explain this.
Update 2 July 2022, 21:55
Using the same simplest project above, friend and I have determined that CSS with styled components breaks when we try to render hours, i.e.:
const hr = now.getHours();
<DateTimeSpan>
{hr}
</DateTimeSpan>
Our suspicion is styled components breaks because hours is rendered in UTC time on the server but locale time on the client.
I'm not sure if this is a bug or if we're supposed to handle this ourselves. Also not sure if this should be asked on Remix or Styled components GitHub issues. I've opened an issue on Remix as well anyway for a start.
Original post
Not sure but could be related to these issues:
I read through the above and a few other pages and all I could think to do was update some dependencies. Here are the possibly relevant ones:
{
"react": "^18.2.0",
"styled-components": "^5.3.5"
"@remix-run/node": "^1.6.1",
"@remix-run/react": "^1.6.1",
"@remix-run/vercel": "^1.6.1",
"@vercel/node": "^2.2.0",
}
My main suspicion is it has to do with styled-components since I've had similar issues on Nextjs before. But my app/root.tsx and app/entry.server.tsx follow this example for styled-components very closely:
// app/root.tsx
export default function App() {
const data = useLoaderData();
return (
<Html lang="en">
<head>
...
{typeof document === "undefined" ? "__STYLES__" : null}
</head>
<Body>
...
</Body>
</Html>
);
}
//app/entry.server.tsx
export default function handleRequest(
request: Request,
responseStatusCode: number,
responseHeaders: Headers,
remixContext: EntryContext
) {
const sheet = new ServerStyleSheet();
let markup = renderToString(
sheet.collectStyles(
<RemixServer context={remixContext} url={request.url} />
)
);
const styles = sheet.getStyleTags();
markup = markup.replace("__STYLES__", styles);
responseHeaders.set("Content-Type", "text/html");
return new Response("<!DOCTYPE html>" + markup, {
status: responseStatusCode,
headers: responseHeaders,
});
}
The biggest difference with the example seems to be that instead of using hydrate
, I use hydrateRoot
as we should for React 18. Not sure if it has any bearing on the problem:
// app/entry.client.tsx
import { RemixBrowser } from "@remix-run/react";
import { hydrateRoot } from "react-dom/client";
hydrateRoot(document, <RemixBrowser />);
The Remix docs on CSS-in-JS libraries says: "You may run into hydration warnings when using Styled Components. Hopefully this issue will be fixed soon." The issue hasn't been resolved so maybe this problem doesn't have a solution yet.
But if the example repo works, then maybe I missed something?