Build an app with Remix and Bun

Remix 3 is a web framework built from standalone packages: a router, HTML rendering, sessions, form parsing, and more. These packages are distributed together as the remix package. Remix 3 is published under the next tag on npm while it is in beta. Its server runs on Bun as-is.

This guide covers Remix 3. To create a Remix 2 project, use create-remix instead.

Scaffold a new project with the Remix CLI.

terminal
bunx remix@next new my-remix-app
• Prepare target directory...
✓ Prepare target directory
• Generate scaffold files...
✓ Generate scaffold files
• Finalize package.json...
✓ Finalize package.json

Created My Remix App at my-remix-app

Then install its dependencies.

terminal
cd my-remix-app
bun install

The generated server.ts creates a node:http server that hands every request to the app's router. Bun runs it directly; pass --watch to restart the server whenever a file it imports changes.

terminal
bun --watch server.ts
Server listening on http://localhost:44100

Open http://localhost:44100 to see the starter page. The routes live in app/routes.ts and app/router.ts. app/actions/home-page.tsx renders the starter home page.


The scripts generated in package.json run the server with Node.js and the remix/node-tsx TypeScript loader. Bun runs TypeScript itself, so point the scripts at bun instead.

package.json
{
  "scripts": {
    "dev": "NODE_ENV=development node --watch --import remix/node-tsx server.ts", 
    "dev": "bun --watch server.ts", 
    "start": "NODE_ENV=production node --import remix/node-tsx server.ts", 
    "start": "NODE_ENV=production bun server.ts" 
  }
}
terminal
bun run dev
$ bun --watch server.ts
Server listening on http://localhost:44100

The generated hmr script (hmr.ts) also loads remix/node-tsx, which relies on module.registerHooks(). Bun does not implement that API yet, so the script fails under Bun. See Node.js compatibility. bun --watch restarts the server on changes instead.


The router is a fetch handler, so you can also serve the app with Bun.serve() instead of node:http.

server.ts
import { router } from "./app/router.ts";

const server = Bun.serve({
  port: 44100,
  fetch: request => router.fetch(request),
});

console.log(`Server listening on ${server.url}`);

See the Remix documentation to learn more.