esbuild-dev-router
v0.7.2
Published
Express middleware for development with esbuild — fast in-memory bundling and automatic page reloading
Maintainers
Readme
esbuild-dev-router
Express middleware for development with esbuild. Bundles your source files in memory, serves them directly, and reloads the page automatically when you save.
Installation
npm install -D esbuild-dev-routerPeer dependency: Your project must also have esbuild installed (any version above 0.17).
Quick Start
Server
import express from 'express';
import devRouter from 'esbuild-dev-router';
import { resolve } from 'path';
const app = express();
app.listen(5000);
// Mount at the root — see "Mount Path" section below
app.use(devRouter({
entryPoints: [resolve(__dirname, 'web/index.ts')]
}));
app.use(express.static(resolve(__dirname, 'public')));HTML
Reference the bundled output by filename. If your entry point is web/index.ts, esbuild will produce index.js:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="/index.css">
</head>
<body>
<script src="/index.js"></script>
</body>
</html>A reload script is automatically injected into every bundle — no extra <script> tag needed. When you save a file, the page reloads.
How It Works
- Builds in memory — esbuild compiles your code with
write: false, so nothing is written to disk. Built files are held in memory and served directly by the router. - Watches for changes — esbuild's watch mode detects file changes and triggers a rebuild.
- Reloads the browser — A small script injected into each bundle opens a Server-Sent Events (SSE) connection to the
/reloaderendpoint. When a build completes, the server emits an event and the browser reloads the page.
Mount Path
The injected reload script connects to /reloader as an absolute path. This means the router must be mounted at the root of your Express app:
// Correct — mounted at root
app.use(devRouter({ ... }));
// Will NOT work — /reloader becomes /assets/reloader
app.use('/assets', devRouter({ ... }));API
devRouter(options)
Accepts an esbuild BuildOptions object and returns an Express Router.
The router handles two routes:
GET /reloader— SSE endpoint that emitsreloadevents after each buildGET /:filename— Serves the in-memory build output, with the correct content type
Default Options
The following defaults are applied and can be overridden through the options object:
| Option | Default | Notes |
|---|---|---|
| bundle | true | |
| target | 'es2022' | |
| sourcemap | 'inline' | |
| minify | false | |
| outdir | 'dist' | Used for path resolution only; files are never written to disk |
| loader | { '.png': 'file', '.jpg': 'file' } | Merged with your custom loaders |
| logLevel | 'info' | |
Options you provide are merged on top of these defaults, with two exceptions:
loader— your loaders are merged with the defaults (not replaced)pluginsandinject— your entries are appended alongside the built-in ones
The write option is always set to false and cannot be overridden.
Imports
// Default import (recommended)
import devRouter from 'esbuild-dev-router';
// Named import (if your setup has trouble with default imports)
import { devRouter } from 'esbuild-dev-router';
// The BuildOptions type is also re-exported for convenience
import { BuildOptions } from 'esbuild-dev-router';Example: Custom Loaders and Target
app.use(devRouter({
entryPoints: [resolve(__dirname, 'web/index.ts')],
target: 'es2020',
loader: {
'.svg': 'file',
'.woff2': 'file',
}
}));This overrides the default target and adds .svg and .woff2 loaders alongside the built-in .png and .jpg loaders.
Console Output
On each build, the router logs:
- The filename of each
.jsand.cssoutput file - A count of other assets (images, fonts, etc.)
Requirements
- Node.js 18+
- esbuild >0.17
- Express 4.x or 5.x
License
Apache License 2.0
