Remix is full speed ahead preparing for our upcoming v2 release! 1.16.0
stabilizes a handful of CSS features, contains a major overhaul to the internals of the new remix dev
server. and fixes a bunch of bugs.
Stabilized CSS Features
CSS Modules/Vanilla Extract/CSS Side-Effect Imports
Version 1.16.0 stabilizes built-in support for CSS Modules, Vanilla Extract and CSS side-effect imports, which were previously only available via future flags (future.unstable_cssModules
, future.unstable_vanillaExtract
, future.unstable_cssSideEffectImports
). In order to use these features, check out our guide to CSS bundling in your project.
PostCSS
Version 1.16.0 stabilizes built-in PostCSS support via the new postcss
option in remix.config.js
. As a result, the future.unstable_postcss
option has also been deprecated. The postcss
option is false
by default, but when set to true
will enable processing of all CSS files using PostCSS if postcss.config.js
is present. For more information on PostCSS support, check out the docs.
If you followed the original PostCSS setup guide for Remix, you may have a folder structure that looks like this, separating your source files from its processed output:
.
├── app
│ └── styles (processed files)
│ ├── app.css
│ └── routes
│ └── index.css
└── styles (source files)
├── app.css
└── routes
└── index.css
After you've enabled the new postcss
option, you can delete the processed files from app/styles
folder and move your source files from styles
to app/styles
:
.
├── app
│ └── styles (source files)
│ ├── app.css
│ └── routes
│ └── index.css
You should then remove app/styles
from your .gitignore
file since it now contains source files rather than processed output.
You can then update your package.json
scripts to remove any usage of postcss
since Remix handles this automatically. For example, if you had followed the original setup guide:
{
"scripts": {
- "dev:css": "postcss styles --base styles --dir app/styles -w",
- "build:css": "postcss styles --base styles --dir app/styles --env production",
- "dev": "concurrently \"npm run dev:css\" \"remix dev\""
+ "dev": "remix dev"
}
}
Tailwind
Version 1.16.0 stabilizes built-in Tailwind support via the new tailwind
option in remix.config.js
. As a result, the future.unstable_tailwind
option has also been deprecated. For more information on Tailwind support, check out the docs.
The tailwind
option is false
by default, but when set to true
will enable built-in support for Tailwind functions and directives in your CSS files if tailwindcss
is installed.
If you followed the original Tailwind setup guide for Remix and want to make use of this feature, you should first delete the generated app/tailwind.css
.
Then, if you have a styles/tailwind.css
file, you should move it to app/tailwind.css
.
rm app/tailwind.css
mv styles/tailwind.css app/tailwind.css
Otherwise, if you don't already have an app/tailwind.css
file, you should create one with the following contents:
@tailwind base;
@tailwind components;
@tailwind utilities;
You should then remove /app/tailwind.css
from your .gitignore
file since it now contains source code rather than processed output.
You can then update your package.json
scripts to remove any usage of tailwindcss
since Remix handles this automatically. For example, if you had followed the original setup guide:
{
"scripts": {
- "build": "run-s \"build:*\"",
+ "build": "remix build",
- "build:css": "npm run generate:css -- --minify",
- "build:remix": "remix build",
- "dev": "run-p \"dev:*\"",
+ "dev": "remix dev",
- "dev:css": "npm run generate:css -- --watch",
- "dev:remix": "remix dev",
- "generate:css": "npx tailwindcss -o ./app/tailwind.css"
}
}
Dev Server Overhaul
🎥 Livestream of Pedro and Kent going over the new dev server
Version 1.16.0
contains a major overhaul to the new dev server, which can be enabled via the future.unstable_dev
flag in remix.config.js
. The Remix dev server now spins up your app server as a managed subprocess. This keeps your development environment as close to production as possible, and also means that the Remix dev server is compatible with any app server.
By default, the dev server will use the Remix App Server, but you opt to use your own app server by specifying the command to run it via the -c
/--command
flag:
remix dev # uses `remix-serve <serve build path>` as the app server
remix dev -c "node ./server.js" # uses your custom app server at `./server.js`
The dev server will:
- force
NODE_ENV=development
and warn you if it was previously set to something else - rebuild your app whenever your Remix app code changes
- restart your app server whenever rebuilds succeed
- handle live reload and HMR + Hot Data Revalidation
App server coordination
In order to manage your app server, the dev server needs to be told what server build is currently being used by your app server. This works by having the app server send a "I'm ready!" message with the Remix server build hash as the payload.
This is handled automatically in Remix App Server and is set up for you via calls to broadcastDevReady
or logDevReady
in the official Remix templates.
If you are not using Remix App Server and your server doesn't call broadcastDevReady
, you'll need to call it in your app server after it is up and running.
For example, in an Express server:
// server.js
// <other imports>
import { broadcastDevReady } from "@remix-run/node";
// Path to Remix's server build directory ('build/' by default)
const BUILD_DIR = path.join(process.cwd(), "build");
// <code setting up your express server>
app.listen(3000, () => {
const build = require(BUILD_DIR);
console.log("Ready: http://localhost:" + port);
// in development, call `broadcastDevReady` _after_ your server is up and running
if (process.env.NODE_ENV === "development") {
broadcastDevReady(build);
}
});
Cloudflare
For CF workers and pages, you'll need to use logDevReady
instead of broadcastDevReady
as the experimental CF runtime won't play nicely with fetch
which is how broadcastDevReady
is implemented.
For examples, here's a sneak peek 👀 of unstable_dev
-enabled templates. Be sure to check out how logDevReady
is called in the server.ts
files for CF workers and for CF pages.
Options
Options priority order is: 1. flags, 2. config, 3. defaults.
Option | flag | config | default |
---|---|---|---|
Command | -c / --command
| command
| remix-serve <server build path>
|
HTTP(S) scheme | --http-scheme
| httpScheme
| http
|
HTTP(S) host | --http-host
| httpHost
| localhost
|
HTTP(S) port | --http-port
| httpPort
| Dynamically chosen open port |
Websocket port | --websocket-port
| websocketPort
| Dynamically chosen open port |
No restart | --no-restart
| restart: false
| restart: true
|
🚨 The --http-*
flags are only used for internal dev server <-> app server communication.
Your app will run on your app server's normal URL.
To set unstable_dev
configuration, replace unstable_dev: true
with unstable_dev: { <options> }
.
For example, to set the HTTP(S) port statically:
// remix.config.js
module.exports = {
future: {
unstable_dev: {
httpPort: 8001,
},
},
};
SSL and custom hosts
You should only need to use the --http-*
flags and --websocket-port
flag if you need fine-grain control of what scheme/host/port for the dev server. If you are setting up SSL or Docker networking, these are the flags you'll want to use.
🚨 Remix will not set up SSL and custom host for you.
The --http-scheme
and --http-host
flag are for you to tell Remix how you've set things up. It is your task to set up SSL certificates and host files if you want those features.
--no-restart
and require
cache purging
If you want to manage server changes yourself, you can use the --no-restart
flag to tell the dev server to refrain from restarting your app server when builds succeed:
remix dev -c "node ./server.js" --no-restart
For example, you could purge the require
cache of your app server to keep it running while picking up server changes. If you do so, you should watch the server build path (build/
by default) for changes and only purge the require
cache when changes are detected.
🚨 If you use --no-restart
, it is your responsibility to call broadcastDevReady
when your app server has picked up server changes.
For example, with chokidar
:
// server.dev.js
const BUILD_PATH = path.resolve(__dirname, "build");
const watcher = chokidar.watch(BUILD_PATH);
watcher.on("all", () => {
// 1. purge require cache
purgeRequireCache();
// 2. load updated server build
const build = require(BUILD_PATH);
// 3. tell dev server that this app server is now ready
broadcastDevReady(build);
});
Other notable changes
- feat: support async
getLoadContext
in all adapters (#6170) - Fix absolute paths in CSS
url()
rules when using CSS Modules, Vanilla Extract and CSS side-effect imports (#5788) - better type discrimination when unwrapping loader return types (#5516)
- pass
AppLoadContext
tohandleRequest
(#5836) - add
@remix-run/node/install
side-effect to allownode --require @remix-run/node/install
(#6132) - short circuit links and meta for routes that are not rendered due to errors (#6107)
- Update Remix for React Router no longer relying on
useSyncExternalStore
(#6121) - Fix false-positive resource route identification if a route only exports a boundary (#6125)
- Updated React Router dependencies to the latest versions:
Changes by Package 🔗
@remix-run/cloudflare
@remix-run/css-bundle
@remix-run/deno
@remix-run/dev
@remix-run/eslint-config
@remix-run/node
@remix-run/react
@remix-run/server-runtime
@remix-run/testing
Full Changelog: 1.15.0...1.16.0