There are a few popular ways to style your markup in the React community. The following have direct support in Remix:
While not built-in to Remix, you can use these also:
Remix does not currently support CSS Modules. However, the Remix compiler uses esbuild which will eventualy have direct support for them. When it supports them, Remix will.
The primary way to style in Remix is to add a <link>
to the document when a route is active with Route Module Links export.
You can load stylesheets from any server, here's an example of loading a modern css reset from unpkg.
// root.tsx
import type { LinksFunction } from "remix";
export let links: LinksFunction = () => {
return [
{
rel: "stylesheet",
href: "https://unpkg.com/modern-css-reset@1.4.0/dist/reset.min.css"
}
];
};
app/
folderAny stylesheets inside the app
folder can be imported into your modules. Remix will:
// root.tsx
import type { LinksFunction } from "remix";
import styles from "./styles/app.css";
export let links: LinksFunction = () => {
return [{ rel: "stylesheet", href: styles }];
};
While not built into Remix's compiler, it is straight forward to use PostCSS and Tailwind. The strategy is straight forward:
postcss
cli directly alongside RemixHere's a quick guide to getting it set up. We encourage you to read the official Tailwind installation instructions as well.
You'll to install them as dev dependencies in your app:
npm install -D tailwindcss@latest postcss@latest autoprefixer@latest postcss-cli@latest
Add postcss.config.js
in the Remix root.
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {}
}
};
And one more config: tailwind.config.js
:
module.exports = {
purge: [
"./app/**/*.tsx",
"./app/**/*.jsx",
"./app/**/*.js",
"./app/**/*.ts"
],
darkMode: false, // or 'media' or 'class'
theme: {
extend: {}
},
variants: {},
plugins: []
};
Add a css file to ./styles/app.css
@tailwind base;
@tailwind components;
@tailwind utilities;
Add some scripts to your package.json
{
"scripts": {
"watch:css": "postcss styles --base styles --dir app/styles -w",
"build:css": "postcss styles --base styles --dir app/styles --env production"
}
}
These commands will process files from ./styles
into ./app/styles
where your Remix modules can import them.
.
├── app
│ └── styles (processed files)
│ ├── app.css
│ └── routes
│ └── index.css
└── styles (source files)
├── app.css
└── routes
└── index.css
We recommend adding app/styles
to your .gitignore
.
Use it! When you're developing styles, open a terminal tab and run your new watch script:
npm run watch:css
When you're building for production, run
npm run build:css
Then import like any other css file:
import type { LinksFunction } from "remix";
import styles from "./styles/app.css";
export let links: LinksFunction = () => {
return [{ rel: "stylesheet", href: styles }];
};
You can use CSS-in-JS libraries like Styled Components, but you need to perform a "double render" in order to extract the styles from the component tree during the server render.
We don't recommend this approach for two reasons:
Styles are embedded into your HTML documents instead of cachable URLs. Browsers can't cache the styles shared between pages and HTML documents now must expire on your CDN with the styles inside of them.
It's a double render. Probably not a big deal, but it's just not needed.
We do recognize that CSS in JS solutions really shine for shared component systems.
Here's some sample code to show how you might use Styled Components with Remix:
First you'll need some context to put your styles on so that your root route can render them.
// app/StylesContext.tsx
import { createContext } from "react";
export default createContext<null | string>(null);
Your entry.server.tsx
will look something like this:
// app/entry.server.tsx
import ReactDOMServer from "react-dom/server";
import type { EntryContext } from "remix";
import { RemixServer } from "remix";
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
) {
// set up the Styled Components sheet
const sheet = new ServerStyleSheet();
// This render is thrown away, it's here simply to let styled components
// extract the styles used
renderToString(
sheet.collectStyles(
<StylesContext.Provider value={null}>
<RemixServer
context={remixContext}
url={request.url}
/>
</StylesContext.Provider>
)
);
// Now that we've rendered, we get the styles out of the sheet
let styles = sheet.getStyleTags();
sheet.seal();
// Finally, we render a second time, but this time we have styles to apply,
// make sure to pass them to `<StylesContext.Provider value>`
let markup = ReactDOMServer.renderToString(
<StylesContext.Provider value={styles}>
<RemixServer
context={remixContext}
url={request.url}
/>
</StylesContext.Provider>
);
responseHeaders.set("Content-Type", "text/html");
return new Response("<!DOCTYPE html>" + markup, {
status: responseStatusCode,
headers: responseHeaders
});
}
Finally, access and render the styles in your root route.
// app/root.tsx
import { Meta, Scripts } from "remix";
import { useContext } from "react";
import StylesContext from "./StylesContext";
export default function Root() {
let styles = useContext(StylesContext);
return (
<html>
<head>
<Meta />
{styles}
</head>
<body>
<Scripts />
</body>
</html>
);
}