This guide will get you familiar with the basic plumbing required to run a Remix app as quickly as possible. While there are many starter templates with different runtimes, deploy targets, and databases, we're going to create a bare-bones project from scratch.
When you're ready to get serious about your Remix project, you might consider starting with a community template. They include TypeScript setups, databases, testing harnesses, authentication, and more. You can find a list of community templates on the Remix Guide Templates page.
mkdir my-remix-app
cd my-remix-app
# install runtime dependencies
npm i @remix-run/node @remix-run/react @remix-run/serve isbot react react-dom
# install dev dependencies
npm i -D @remix-run/dev
mkdir app
touch app/root.jsx
app/root.jsx
is what we call the "Root Route". It's the root layout of your entire app. Here's the basic set of elements you'll need for any project:
import {
Links,
Meta,
Outlet,
Scripts,
} from "@remix-run/react";
export default function App() {
return (
<html>
<head>
<link
rel="icon"
href="data:image/x-icon;base64,AA"
/>
<Meta />
<Links />
</head>
<body>
<h1>Hello world!</h1>
<Outlet />
<Scripts />
</body>
</html>
);
}
First build the app for production:
npx remix build
You should now see a build/
folder (the server version of your app) and public/build
folder (the browser version) with some build artifacts in them. (This is all configurable.)
πΏ Run the app with remix-serve
First you will need to specify the type in package.json
as module so that remix-serve
can run your app.
{
"type": "module"
// ...
}
Now you can run your app with remix-serve
:
# note the dash!
npx remix-serve build/index.js
You should be able to open up http://localhost:3000 and see the "hello world" page.
Aside from the unholy amount of code in node_modules
, our Remix app is just one file:
βββ app
β βββ root.jsx
βββ package.json
The build/
directory created by remix build
is just a module that you run inside a server like Express, Cloudflare Workers, Netlify, Vercel, Fastly, AWS, Deno, Azure, Fastify, Firebase, ... anywhere.
If you don't care to set up your own server, you can use remix-serve
. It's a simple express-based server maintained by the Remix team. However, Remix is specifically designed to run in any JavaScript environment so that you own your stack. It is expected many βif not mostβ production apps will have their own server. You can read more about this in Runtimes, Adapters, and Stacks.
Just for kicks, let's stop using remix-serve
and use express instead.
πΏ Install Express and the Remix Express adapter
npm i express @remix-run/express
# not going to use this anymore
npm uninstall @remix-run/serve
πΏ Create an Express server
touch server.mjs
import { createRequestHandler } from "@remix-run/express";
import express from "express";
// notice that the result of `remix build` is "just a module"
import * as build from "./build/index.js";
const app = express();
app.use(express.static("public"));
// and your app is "just a request handler"
app.all("*", createRequestHandler({ build }));
app.listen(3000, () => {
console.log("App listening on http://localhost:3000");
});
πΏ Run your app with express
node server.mjs
Now that you own your server, you can debug your app with whatever tooling your server has. For example, you can inspect your app with chrome devtools with the Node.js inspect flag:
node --inspect server.mjs
Instead of stopping, rebuilding, and starting your server all the time, you can run Remix in development. This enables instant feedback to changes in your app with React Refresh (Hot Module Replacement) and Remix Hot Data Revalidation.
First add a dev command in package.json
that will run remix dev
:
πΏ Add a "scripts" entry to package.json
{
"scripts": {
"dev": "remix dev -c \"node server.mjs\""
}
// ...
}
This will start the Remix development server which will watch your files for changes and rebuild your app. The -c
flag tell it how to start your actual application server.
When files change, Remix will restart your server for you, but because you own your server, you also have to tell Remix when it has restarted so Remix can safely send the hot updates to the browser.
πΏ Add broadcastDevReady
to your server
import { createRequestHandler } from "@remix-run/express";
import { broadcastDevReady } from "@remix-run/node";
import express from "express";
// notice that the result of `remix build` is "just a module"
import * as build from "./build/index.js";
const app = express();
app.use(express.static("public"));
// and your app is "just a request handler"
app.all("*", createRequestHandler({ build }));
app.listen(3000, () => {
if (process.env.NODE_ENV === "development") {
broadcastDevReady(build);
}
console.log("App listening on http://localhost:3000");
});
And finally, let's connect your UI in the browser to receive those broadcasts:
import {
Links,
LiveReload,
Meta,
Outlet,
Scripts,
} from "@remix-run/react";
export default function App() {
return (
<html>
<head>
<link
rel="icon"
href="data:image/x-icon;base64,AA"
/>
<Meta />
<Links />
</head>
<body>
<h1>Hello world!</h1>
<Outlet />
<Scripts />
<LiveReload />
</body>
</html>
);
}
πΏ Start the dev server
npm run dev
Now you can work on your app with immediate feedback. Give it a shot, change the text in root.jsx
and watch!
There are default magic files Remix is using that most apps don't need to mess with, but if you want to customize Remix's entry points to the server and browser you can run remix reveal
and they'll get dumped into your project.
npx remix reveal
Entry file entry.client created at app/entry.client.tsx.
Entry file entry.server created at app/entry.server.tsx.
Congrats, you can add Remix to your resume! Summing things up, we've learned:
remix build
and remix dev
compile your app into two things:
In general, Remix is a bit "guts out". A few minutes of boilerplate but now you own your stack.
What's next?