Skip to content

Instantly share code, notes, and snippets.

@kiliman
Last active January 19, 2022 15:44
Show Gist options
  • Save kiliman/e8cb5b1ca6b4afa8ef5930aace00428a to your computer and use it in GitHub Desktop.
Save kiliman/e8cb5b1ca6b4afa8ef5930aace00428a to your computer and use it in GitHub Desktop.
Remix Run and Tailwind CSS config with jit support

Remix and Tailwind CSS config with jit support

Update 1: include livereload to refresh browser automatically on change

Update 2: clear express cache on each request instead of restarting on change

Update 3: explain reasons for livereload settings and also the double build

Put your tailwind.css directives file in root /styles folder. The css:build script will generate /app/styles/tailwind.css.

Reference the generated CSS from root.tsx using import tailwind from 'url:./styles/tailwind.css'

NOTE: If you update to Tailwind CSS v2.1+, it has built-in JIT support. Just add mode: 'jit' to your config.

Update 1

Add LiveReload to your pm2.config. You can update the wait time as needed to allow Remix/Tailwind to complete its build.

Import the <LiveReload/> component in root.tsx. This will add the required <script/> tag in development.

Update 2

Update server.js to clear require cache on each request. Only restart express if server.js changes.

Update 3

I added comments to my livereload (pm2.config) settings.

You may see Remix build multiple times when saving a file. It watches for changes, so when you save a route file, it will start the rebuild. But Tailwind JIT also watches files and will regenerate the css file, which remix also sees and that causes remix to rebuild. It would be nice if we could tell Remix to wait a bit before starting a rebuild, but with esbuild, it is so fast, it probably wouldn't make much difference.

export default function () {
return process.env.NODE_ENV === 'development' ? (
<script src="http://localhost:35729/livereload.js?snipver=1"></script>
) : null
}
"scripts": {
"css:watch": "postcss styles/**/*.css --base styles --dir app/styles --w",
"css:build": "postcss styles/**/*.css --base styles --dir app/styles --env production",
},
"devDependencies": {
"livereload": "^0.9.3",
"postcss-cli": "^8.3.1",
"tailwindcss": "^2.1.1",
}
module.exports = {
apps: [
{
name: 'Express',
script: 'server.js',
watch: ['server.js'],
watch_options: {
followSymlinks: false,
},
env: {
NODE_ENV: 'development',
},
},
{
name: 'Remix',
script: 'remix run',
ignore_watch: ['.'],
env: {
NODE_ENV: 'development',
},
},
{
name: 'PostCSS',
script: 'npm run css:watch',
ignore_watch: ['.'],
env: {
NODE_ENV: 'development',
},
},
{
name: 'LiveReload',
// wait for remix to build, also exclude CSS that tailwind rebuilds
// since we need to reload the entire page anyway
script: 'livereload --wait 500 --exclusions css',
ignore_watch: ['.'],
env: {
NODE_ENV: 'development',
},
},
],
}
module.exports = {
plugins: [
require('tailwindcss'),
require('autoprefixer'),
]
}
import LiveReload from './components/LiveReload'
import tailwind from './styles/tailwind.css'
export const links: LinksFunction = () => {
return [{ rel: 'stylesheet', href: tailwind }]
}
...
<body>
<Outlet />
<Scripts />
<LiveReload />
</body>
const path = require('path')
...
// flush cache on every request vs restarting express
if (process.env.NODE_ENV !== 'production') {
let cwd = process.cwd()
app.all('*', (req, res) => {
for (let key in require.cache) {
if (key.startsWith(path.join(cwd, 'build'))) {
delete require.cache[key]
if (process.env.DEBUG) console.warn('deleted', key)
}
}
return createRequestHandler({
build: require('./build'),
})(req, res)
})
} else {
app.all(
'*',
createRequestHandler({
build: require('./build'),
}),
)
}
const defaultTheme = require('tailwindcss/defaultTheme')
module.exports = {
mode: 'jit',
purge: ['./app/**/*.{js,ts,tsx,md,mdx}', './remix.config.js'],
darkMode: 'media', // or 'media' or 'class'
theme: {
extend: {
fontFamily: {
sans: ['Inter var', ...defaultTheme.fontFamily.sans],
},
},
},
variants: {
extend: {},
},
plugins: [],
}
@kentcdodds
Copy link

Can you please explain why you have -x css in the livereload command?

@kiliman
Copy link
Author

kiliman commented Apr 12, 2021

Yes. Because Tailwind JIT was causing the CSS to be regenerated, LiveReload was detecting this and apparently has a default behavior of only reloading the .css file (called applyCSSLive in config... not available from CLI). See https://github.com/napcs/node-livereload#server-api

applyCSSLive tells LiveReload to reload CSS files in the background instead of refreshing the page. The default for this is true.

This was causing the livereload script to fire twice (once for the CSS and again for reloading the page). By excluding the css file, it would only do the single one for reloading the page. So this is more an optimization than anything.

Also, before we had Express restarting after each build, so the server was not ready when livereload was requesting the CSS file (despite the wait option 😦), so the flag also prevented errors in my Network log. But now that we only clear the require cache, the CSS reload is mostly harmless. However, typically the CSS is updated because you updated a route page, so most likely that will trigger a rebuild anyway where you want a reload.

If you were using manual CSS files, then I can see it being useful to only reload the CSS instead of the full page.

@kentcdodds
Copy link

Makes sense! Thank you :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment