|
| 1 | +--- |
| 2 | +title: "@remix-run/dev CLI (v2)" |
| 3 | +order: 2 |
| 4 | +new: true |
| 5 | +--- |
| 6 | + |
| 7 | +The Remix CLI comes from the `@remix-run/dev` package. It also includes the compiler. Make sure it is in your `package.json` `devDependencies` so it doesn't get deployed to your server. |
| 8 | + |
| 9 | +To get a full list of available commands and flags, run: |
| 10 | + |
| 11 | +```sh |
| 12 | +npx @remix-run/dev -h |
| 13 | +``` |
| 14 | + |
| 15 | +## `remix build` |
| 16 | + |
| 17 | +Builds your app for production. This command will set `process.env.NODE_ENV` to `production` and minify the output for deployment. |
| 18 | + |
| 19 | +```sh |
| 20 | +remix build |
| 21 | +``` |
| 22 | + |
| 23 | +### Options |
| 24 | + |
| 25 | +| Option | flag | config | default | |
| 26 | +| ---------------------------------------- | ------------- | ------ | ------- | |
| 27 | +| Generate sourcemaps for production build | `--sourcemap` | N/A | `false` | |
| 28 | + |
| 29 | +## `remix dev` |
| 30 | + |
| 31 | +Builds your app and spins up the Remix dev server alongside your app server. |
| 32 | + |
| 33 | +The dev server will: |
| 34 | + |
| 35 | +1. Set `NODE_ENV` to `development` |
| 36 | +2. Watch your app code for changes and trigger rebuilds |
| 37 | +3. Restart your app server whenever rebuilds succeed |
| 38 | +4. Send code updates to the browser via Live Reload and HMR + Hot Data Revalidation |
| 39 | + |
| 40 | +### With `remix-serve` |
| 41 | + |
| 42 | +Enable the v2 dev server: |
| 43 | + |
| 44 | +```js filename=remix.config.js |
| 45 | +module.exports = { |
| 46 | + future: { |
| 47 | + v2_dev: true, |
| 48 | + }, |
| 49 | +}; |
| 50 | +``` |
| 51 | + |
| 52 | +That's it! |
| 53 | + |
| 54 | +### With custom app server |
| 55 | + |
| 56 | +If you used a template to get started, hopefully it has integration with the v2 dev server out-of-the-box. |
| 57 | +If not, you can follow these steps to integrate your project with `v2_dev`: |
| 58 | + |
| 59 | +1. Enable the v2 dev server: |
| 60 | + |
| 61 | +```js filename=remix.config.js |
| 62 | +module.exports = { |
| 63 | + future: { |
| 64 | + v2_dev: true, |
| 65 | + }, |
| 66 | +}; |
| 67 | +``` |
| 68 | + |
| 69 | +2. Replace your dev scripts in `package.json` and use `-c` to specify your app server command: |
| 70 | + |
| 71 | +```json |
| 72 | +{ |
| 73 | + "dev": "remix dev -c 'node ./server.js'" |
| 74 | +} |
| 75 | +``` |
| 76 | + |
| 77 | +3. Ensure `broadcastDevReady` is called when your app server is up and running: |
| 78 | + |
| 79 | +```js filename=server.js lines=[12,25-27] |
| 80 | +import path from "node:path"; |
| 81 | + |
| 82 | +import { broadcastDevReady } from "@remix-run/node"; |
| 83 | +import express from "express"; |
| 84 | + |
| 85 | +const BUILD_DIR = path.resolve(__dirname, "build"); |
| 86 | +const build = require(BUILD_DIR); |
| 87 | + |
| 88 | +const app = express(); |
| 89 | + |
| 90 | +// ... code for setting up your express app goes here ... |
| 91 | + |
| 92 | +app.all( |
| 93 | + "*", |
| 94 | + createRequestHandler({ |
| 95 | + build, |
| 96 | + mode: process.env.NODE_ENV, |
| 97 | + }) |
| 98 | +); |
| 99 | + |
| 100 | +const port = 3000; |
| 101 | +app.listen(port, () => { |
| 102 | + console.log(`👉 http://localhost:${port}`); |
| 103 | + |
| 104 | + if (process.env.NODE_ENV === "development") { |
| 105 | + broadcastDevReady(build); |
| 106 | + } |
| 107 | +}); |
| 108 | +``` |
| 109 | + |
| 110 | +<docs-info> |
| 111 | + |
| 112 | +For CloudFlare, use `logDevReady` instead of `broadcastDevReady`. |
| 113 | + |
| 114 | +Why? `broadcastDevReady` uses `fetch` to send a ready message to the dev server, |
| 115 | +but CloudFlare does not support async I/O like `fetch` outside of request handling. |
| 116 | + |
| 117 | +</docs-info> |
| 118 | + |
| 119 | +### Options |
| 120 | + |
| 121 | +Options priority order is: 1. flags, 2. config, 3. defaults. |
| 122 | + |
| 123 | +| Option | flag | config | default | |
| 124 | +| --------------- | ------------------ | ---------------- | ------------------------------------------------- | |
| 125 | +| Command | `-c` / `--command` | `command` | `remix-serve <server build path>` | |
| 126 | +| No restart | `--no-restart` | `restart: false` | `restart: true` | |
| 127 | +| Scheme | `--scheme` | `scheme` | `https` if TLS key/cert are set, otherwise `http` | |
| 128 | +| Host | `--host` | `host` | `localhost` | |
| 129 | +| Port | `--port` | `port` | Dynamically chosen open port | |
| 130 | +| TLS key | `--tls-key` | `tlsKey` | N/A | |
| 131 | +| TLS certificate | `--tls-cert` | `tlsCert` | N/A | |
| 132 | + |
| 133 | +<docs-info> |
| 134 | + |
| 135 | +The scheme/host/port options only affect the Remix dev server, and **do not affect your app server**. |
| 136 | +Your app will run on your app server's normal URL. |
| 137 | + |
| 138 | +You most likely won't want to configure the scheme/host/port for the dev server, |
| 139 | +as those are implementation details used internally for hot updates. |
| 140 | +They exist in case you need fine-grain control, for example Docker networking or using specific open ports. |
| 141 | + |
| 142 | +</docs-info> |
| 143 | + |
| 144 | +For example, to override the port used by the dev server via config: |
| 145 | + |
| 146 | +```js filename=remix.config.js |
| 147 | +module.exports = { |
| 148 | + future: { |
| 149 | + v2_dev: { |
| 150 | + port: 8001, |
| 151 | + }, |
| 152 | + }, |
| 153 | +}; |
| 154 | +``` |
| 155 | + |
| 156 | +### Keep app server running across rebuilds |
| 157 | + |
| 158 | +By default, the Remix dev server restarts your app server when rebuilds occur. |
| 159 | +This is a simple way to ensure that your app server is up-to-date with the latest code changes. |
| 160 | + |
| 161 | +If you'd like to opt-out of this behavior use the `--no-restart` flag: |
| 162 | + |
| 163 | +```sh |
| 164 | +remix dev --no-restart -c 'node ./server.js' |
| 165 | +``` |
| 166 | + |
| 167 | +🚨 BUT that means you are now on the hook for applying changes to your running app server _and_ telling the dev server when those changes have been applied. |
| 168 | + |
| 169 | +> With great power comes great responsibility. |
| 170 | +
|
| 171 | +Check out our [templates][templates] for examples on how to use `import` cache busting to apply code changes to your app server while it keeps running. |
| 172 | + |
| 173 | +If you're using CJS but looking at an ESM template, you'll need to swap out `import` cache busting with `require` cache busting: |
| 174 | + |
| 175 | +```diff |
| 176 | +- const stat = fs.statSync(BUILD_DIR); |
| 177 | +- build = import(BUILD_DIR + "?t=" + stat.mtimeMs); |
| 178 | ++ for (const key in require.cache) { |
| 179 | ++ if (key.startsWith(BUILD_DIR)) { |
| 180 | ++ delete require.cache[key]; |
| 181 | ++ } |
| 182 | ++ } |
| 183 | ++ build = require(BUILD_DIR) |
| 184 | +``` |
| 185 | + |
| 186 | +#### Pick up changes from other packages |
| 187 | + |
| 188 | +If you are using a monorepo, you might want Remix to perform hot updates not only when your app code changes, but whenever you change code in any of your apps dependencies. |
| 189 | + |
| 190 | +For example, you could have a UI library package (`packages/ui`) that is used within your Remix app (`packages/app`). |
| 191 | +To pick up changes in `packages/ui`, you can configure [watchPaths][watch-paths] to include your packages. |
| 192 | + |
| 193 | +#### Keep in-memory data and connections across rebuilds |
| 194 | + |
| 195 | +Every time you re-import code to apply changes to your app server, that code will be run. |
| 196 | +Rerunning each changed module works great in most cases, but sometimes you want to want to keep stuff around. |
| 197 | + |
| 198 | +For example, it'd be nice if your app only connected to your database once and kept that connection around across rebuilds. |
| 199 | +But since the connection is held in-memory, re-imports will wipe those out and cause your app to reconnect. |
| 200 | + |
| 201 | +Luckily, there's a trick to get around this: use `global` as a cache for keeping things in-memory across rebuilds! |
| 202 | +Here's a nifty utility adapted from [Jon Jensen's code][jenseng-code] for [his Remix Conf 2023 talk][jenseng-talk]: |
| 203 | + |
| 204 | +```ts filename=app/utils/remember.ts |
| 205 | +export function remember<T>(key: string, value: T) { |
| 206 | + const g = global as any; |
| 207 | + g.__singletons ??= {}; |
| 208 | + g.__singletons[key] ??= value; |
| 209 | + return g.__singletons[key]; |
| 210 | +} |
| 211 | +``` |
| 212 | + |
| 213 | +And here's how to use it to keep stuff around across rebuilds: |
| 214 | + |
| 215 | +```ts filename=app/utils/db.server.ts |
| 216 | +import { PrismaClient } from "@prisma/client"; |
| 217 | + |
| 218 | +import { remember } from "~/utils/remember"; |
| 219 | + |
| 220 | +// hard-code a unique key so we can look up the client when this module gets re-imported |
| 221 | +export const db = remember("db", new PrismaClient()); |
| 222 | +``` |
| 223 | + |
| 224 | +### How to set up local HTTPS |
| 225 | + |
| 226 | +For this example, let's use [mkcert][mkcert]. |
| 227 | +After you have it installed, make sure to: |
| 228 | + |
| 229 | +- Create a local Certificate Authority if you haven't already done so |
| 230 | +- Use `NODE_EXTRA_CA_CERTS` for Node compatibility |
| 231 | + |
| 232 | +```sh |
| 233 | +mkcert -install # create a local CA |
| 234 | +export NODE_EXTRA_CA_CERTS="$(mkcert -CAROOT)/rootCA.pem" # tell Node to use our local CA |
| 235 | +``` |
| 236 | + |
| 237 | +Now, create the TLS key and certificate: |
| 238 | + |
| 239 | +```sh |
| 240 | +mkcert -key-file key.pem -cert-file cert.pem localhost |
| 241 | +``` |
| 242 | + |
| 243 | +👆 You can change `localhost` to something else if you are using custom hostnames. |
| 244 | + |
| 245 | +Next, use the `key.pem` and `cert.pem` to get HTTPS working locally with your app server. |
| 246 | +This depends on what you are using for your app server. |
| 247 | +For example, here's how you could use HTTPS with an Express server: |
| 248 | + |
| 249 | +```ts filename=server.js |
| 250 | +import fs from "node:fs"; |
| 251 | +import https from "node:https"; |
| 252 | + |
| 253 | +import express from "express"; |
| 254 | + |
| 255 | +const app = express(); |
| 256 | + |
| 257 | +// ... code setting up your express app goes here ... |
| 258 | + |
| 259 | +// |
| 260 | +``` |
0 commit comments