-
-
Save ryanflorence/b7f1653404b350483909609e435a8a2d to your computer and use it in GitHub Desktop.
import ReactDOMServer from "react-dom/server"; | |
import type { EntryContext } from "@remix-run/core"; | |
import Remix from "@remix-run/react/server"; | |
import { renderToString } from "react-dom/server"; | |
import { ServerStyleSheet } from "styled-components"; | |
import StylesContext from "./stylesContext"; | |
export default function handleRequest( | |
request: Request, | |
responseStatusCode: number, | |
responseHeaders: Headers, | |
remixContext: EntryContext | |
) { | |
const sheet = new ServerStyleSheet(); | |
// first pass to collect styles | |
renderToString( | |
sheet.collectStyles( | |
<StylesContext.Provider value={null}> | |
<Remix context={remixContext} url={request.url} /> | |
</StylesContext.Provider> | |
) | |
); | |
// get the styles | |
let styles = sheet.getStyleTags(); | |
sheet.seal(); | |
// second time with the styles on context | |
let markup = ReactDOMServer.renderToString( | |
<StylesContext.Provider value={styles}> | |
<Remix context={remixContext} url={request.url} /> | |
</StylesContext.Provider> | |
); | |
return new Response("<!DOCTYPE html>" + markup, { | |
status: responseStatusCode, | |
headers: { | |
...Object.fromEntries(responseHeaders), | |
"Content-Type": "text/html" | |
} | |
}); | |
} |
import { Meta, Scripts } from "@remix-run/react"; | |
import { useContext } from "react"; | |
import StylesContext from "./stylesContext"; | |
export default function Root() { | |
// get styles from context | |
let styles = useContext(StylesContext); | |
return ( | |
<html> | |
<head> | |
<Meta /> | |
{styles} | |
</head> | |
<body> | |
<Scripts /> | |
</body> | |
</html> | |
); | |
} |
import { createContext } from "react"; | |
export default createContext<null | string>(null); |
same for me
Same error here
I faced the same issue. As a workaround you can do the following:
-
Set up styled-components as shown in the Remix documentation.
-
Inside your
root.tsx
in the<head/>
tag instead of{styles}
put the following:
{styles !== null && (<style dangerouslySetInnerHTML={{ __html:
</style>${styles}<style>}} /> )}
Please note that this is not a safe solution! According to React documentation:
In general, setting HTML from code is risky because it’s easy to inadvertently expose your users to a cross-site scripting (XSS) attack.
That is why I do not recommend using it in production. It can significantly speed up development process though (you don't have to restart dev enviroment each time you make a change). I guess we have to wait until the creators of Remix provide us with dedicated solution.
Thanks, @MilanKrupa for sharing the workaround. For the first time, the pages of my app were rendering with proper styles because of SSR I believe but after client-side hydration, the styles were going away.
I was able to fix the issue by avoiding "dangerouslySetInnerHTML" because that is prone to XSS. here us my snippet of code from root. tsx
: styles !== null && <style>{styles}</style>}
Thanks to @ryanflorence for sharing the original integration snippet.
why not just inject the style tags at the end of the <head />
like so?
const markupWithStyles = markup.replace(
/<\s*\/\s*head\s*>/gm,
`${styleTags}</head>`
);
return new Response("<!DOCTYPE html>" + markupWithStyles, {
status: responseStatusCode,
headers: responseHeaders,
});
edit: nvm this solution still causes the loss of styles upon rehydration
While on dev mode it's only styling on the first request right? Am I doing something wrong?