github remix-run/remix remix@1.16.0
v1.16.0

latest releases: v0.0.0-nightly-417ecc42b-20241109, remix@2.14.0, @remix-run/testing@2.14.0...
18 months ago

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 to handleRequest (#5836)
  • add @remix-run/node/install side-effect to allow node --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 🔗


Full Changelog: 1.15.0...1.16.0

Don't miss a new remix release

NewReleases is sending notifications on new releases.