v0.14.0 Release Notes

This release includes some significant improvements to the way Remix apps are deployed to production. It includes two major improvements to a production Remix app:

  • No more dev-only dependencies being deployed to your server
  • No more dynamic requires

These are some pretty major changes to the underlying architecture of Remix that fixes support for several cloud providers and opens the door to deploying to many more.

Specifially, this release fixes deployments on both Architect (broken in 0.10) and Vercel (who recently changed the way they do deploys).

Upgrading Summary

To upgrade from Remix 0.13:

  • Remove @remix-run/cli from your dependencies in package.json and replace it with @remix-run/dev in your devDependencies
  • Use createRequestHandler({ build: require("./build") }) in server.js
  • Add a postinstall step to run remix build
  • In dev mode, watch build/assets.json to know when to restart the server

As always, remember to use the starters as your guide. We currently have starter repos for:


Before Remix 0.14, we had both development and production dependencies in the same package: @remix-run/core. Having everything in one package helped us bootstrap the project quickly, but also became a burden over time. For example, as we added features to the Remix compiler, which is only needed in development, the overall size of the dependencies Remix needed in a production deployment grew. So, we knew we needed to split out all of Remix's development dependencies into a separate package. In addition, some of our dev dependencies require binaries that we are not able to deploy to AWS. Separating them out into a dev-only package fixes production deploys on AWS.

Another issue with deploying Remix to production before this release is that it required you to deploy your remix.config.js file (and your app directory!) alongside your build directory. Remix would read the config and reconstruct the route hierarchy at runtime so that it knew about any dynamic routes you created using config.routes. It would also dynamically require all modules needed to run your app, which was a non-starter on hosts like Netlify (and, suddenly as of this week, Vercel 🤷‍♂️). We knew we needed to get rid of all dynamic requires in production. You shouldn't have to deploy your source code, just the build artifacts.

So ... this release splits up our core dependency and completely alters the way we load modules and deploy to production.

No sweat, right? 😅

The big winner is that your production deploys are going to be more streamlined (no dev dependencies) and we are laying the groundwork for being able to deploy Remix to more providers by eliminating dynamic requires.


Despite the significant changes on our side, upgrading your app from Remix 0.13 should be fairly straightforward.

First, remove @remix-run/cli from your dependencies in package.json and replace it with @remix-run/dev in your devDependencies.

$ npm remove @remix-run/cli
$ npm add -D @remix-run/dev

Going forward, @remix-run/dev will contain everything you need to develop a Remix app. It should only ever be in devDependencies so it doesn't end up in your production environment. We even named it dev to help you remember :D

Next, we need to let the Remix server know about our app. In Remix 0.14, your entire app is compiled down to a single module in build/index.js with static require calls to load the rest of the app. This means you can load the entire app using require("./build") (or whatever your config.serverBuildDirectory is).

To upgrade, open up server.js (or your serverless function module) and add the build key to your server's createRequestHandler:

  // Add this line!
  build: require("./build"),
  getLoadContext() {
    // ...

Note: You can see how we did it in the Express starter.

This line requires the entire app and passes it to your server. Of course, this means you'll need to actually build the app before you can start the server, so you may want to add a postinstall hook to your package.json so that your app is ready to go after a fresh npm install:

  "scripts": {
    "postinstall": "remix build"

If you're using Vercel, Architect, or Firebase, this postintall hook isn't necessary, just make sure your build finishes before you open the app in the browser.

In Express apps, you'll probably also want to update your file watcher in development. We currently recommend watching build/assets.json to know when to restart your server.

In the Express starter we use PM2. The relevant portion of the config looks like this:

module.exports = {
  apps: [
      script: "server.js",
      watch: ["build/assets.json"]

Note: There are actually 2 builds going on (server and client), but the client build always takes longer since it has to bundle dependencies as well as your app code. So that's why we recommend watching build/assets.json. We are, however, working on improving our build times, so this recommendation may change soon.

If you're using Vercel or Architect, you don't need to set up any watchers, these providers dev servers already handle the file watching.

Vercel Notes

The build config and remix config in Vercel deploys were a bit involved because of our dynamic requires, you can now greatly simplify it, and even use Vercel's auto-deploys from GitHub now.

Your remix.config.js no longer needs to branch on any environment variables, it can be simple again:

module.exports = {
  appDirectory: "app",
  browserBuildDirectory: "public/build",
  publicPath: "/build/",
  serverBuildDirectory: "build",
  devServerPort: 8002

Your vercel.json can likewise be simplified by removing the includeFiles option in your builds config:

  "builds": [
      "src": "index.js",
      "use": "@vercel/node"
  // etc.

Because your app modules are all in the require graph now, we don't need to provide any hints to Vercel about what to deploy, it knows just by looking at your index file.

Other Changes

A few other miscellaneous changes in this release that you may be interested in:

  • The compiler now uses an on-disk cache that defaults to the .cache directory in the root (sibling to remix.config.js). If you want to put it somewhere else, use config.cacheDirectory in your remix.config.js
  • The compiler doesn't replace any process.env.NODE_ENV strings in your server code anymore, so you should get that value from the actual process that is running your server

Enjoy your slimmed down production builds!