-
Hi there 🙂 Do we have any example on how to make Chakra UI (which uses Emotion under the hood) work with Remix ? I'm looking for somehing similar to remix.run/docs/en/v1/guides/styling#css-in-js-libraries which shows the way to make styled-components work, but for Emotion I guess |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments
-
I got it running (even without rendering the current route twice). The trick is to you some sort of placeholder inside the head of the rendered string. This placeholder gets then replaced with the actual styles from the emotion cache. // app/entry.server.tsx
import { CacheProvider } from "@emotion/react";
import createEmotionServer from "@emotion/server/create-instance";
import { renderToString } from "react-dom/server";
import type { EntryContext } from "remix";
import { RemixServer } from "remix";
import createEmotionCache from "./utils/create-emotion-cache";
import StylesPlaceholderContext from "./utils/styles-placeholder-context";
export default function handleRequest(
request: Request,
responseStatusCode: number,
responseHeaders: Headers,
remixContext: EntryContext
) {
const cache = createEmotionCache();
const { extractCriticalToChunks, constructStyleTagsFromChunks } =
createEmotionServer(cache);
const placeholder = "%emotion-placeholder%";
let markup = renderToString(
<CacheProvider value={cache}>
<StylesPlaceholderContext.Provider value={placeholder}>
<RemixServer context={remixContext} url={request.url} />
</StylesPlaceholderContext.Provider>
</CacheProvider>
);
const emotionChunks = extractCriticalToChunks(markup);
const emotionCss = constructStyleTagsFromChunks(emotionChunks);
markup = markup.replace(placeholder, emotionCss);
responseHeaders.set("Content-Type", "text/html");
return new Response("<!DOCTYPE html>" + markup, {
status: responseStatusCode,
headers: responseHeaders,
});
} // app/entry.client.tsx
import { CacheProvider } from "@emotion/react";
import { hydrate } from "react-dom";
import { RemixBrowser } from "remix";
import createEmotionCache from "./common/utils/create-emotion-cache";
const cache = createEmotionCache();
hydrate(
<CacheProvider value={cache}>
<RemixBrowser />
</CacheProvider>,
document
); // app/utils/create-emotion-cache.ts
import createCache from "@emotion/cache";
export default function createEmotionCache() {
return createCache({ key: "css" });
} // app/styles-placeholder-context.tsx
import { createContext } from "react";
export default createContext<string | null>(null); // app/root.tsx
import { useContext } from "react";
import { LiveReload, Outlet, Links, Scripts, Meta } from "remix";
import StylesPlaceholderContext from "./common/utils/styles-placeholder-context";
export default function App() {
const placeholder = useContext(StylesPlaceholderContext);
return (
<html lang="en">
<head>
<meta charSet="utf-8" />
<Meta />
<title>Remix App</title>
<Links />
{placeholder}
</head>
<body>
<Outlet />
<Scripts />
{process.env.NODE_ENV === "development" ? <LiveReload /> : null}
</body>
</html>
);
} Let me know, if this works for you. |
Beta Was this translation helpful? Give feedback.
-
Btw. there is already a documentation on how to integrate Material-UI into Remix: https://github.com/mui-org/material-ui/tree/master/examples/remix-with-typescript Due to Material-UI using emotion as its base, this should work as a good starting point, too. (Though I prefer the single render and something like the placeholder I posted above.) |
Beta Was this translation helpful? Give feedback.
I got it running (even without rendering the current route twice). The trick is to you some sort of placeholder inside the head of the rendered string. This placeholder gets then replaced with the actual styles from the emotion cache.