ts-r-dev
Dev server with hot reload. Replaces the Vite dev server.
- Pre-alpha
- Works from source
ts-r-dev serves your app as unbundled ES modules, transforms only the file you save, and pushes the update to the browser. It speaks Vite’s hot-reload protocol, so code written against import.meta.hot works unchanged. It replaces vite dev for the dev loop, not vite build.
10× faster hot reload
What switching looks like
Nothing is on npm yet. Once it is, switching looks like this: ts-r-dev reads the config you already have, so the change is to the command, not the project.
"scripts": {
- "dev": "vite",
+ "dev": "ts-r-dev",
}
What works, and what doesn’t yet
Works today
- Hot module replacement with Vite’s semantics: self-accepting modules, hot.accept, hot.decline, hot.invalidate, hot.data, hot.dispose and hot.on
- TypeScript, TSX and JSX through ts-r’s own transform; JSON, assets and CSS, with CSS updates applied without a reload
- React Fast Refresh when a React Refresh runtime is installed; top-level components keep their state across edits
- Dependencies pre-bundled in one code-split batch at startup, cached per lockfile
- An error overlay for transform and resolve errors; the server never crashes on a bad file
- pnpm workspace packages treated as source and hot-updated like app code
Not yet
- No vite.config loading and no Vite plugin API
- No --open, HTTPS, proxy, CORS headers or compression; HTTP/1.1 only
- CSS @import and CSS modules aren’t handled
- Inline module scripts in HTML and non-literal import(expr) aren’t rewritten
- Windows is untested
Measured
Every run, on the same reference app as the home page, with the command and commit that recorded it.
ts-r-dev against Vite 8 on a real app
private-app's web app (a Next.js app; the harness serves its React client components with Fast Refresh) served by ts-r-dev and by Vite 8.3.4 with @vitejs/plugin-react 6.1.2. Start is process spawn until the header, form and footer are in the page; cold means an empty ts-r cache and no node_modules/.vite. Both kept the form's state through all 53 edits with 0 full reloads.
- ts-r
- Other tools
- ts-r
- Other tools
- ts-r
- Other tools
How we measured
- Measured
- Headless Chrome drives both servers: 5 cold and 5 warm starts (median), then 50 measured edits of sign-in-form.tsx after 3 warm-ups, timed in the browser. RSS is the whole server process group.
- Machine
- Apple M-series Mac, macOS arm64, release build (the exact chip was not recorded)
- Software
- ts-r-dev release build; Vite 8.3.4 with @vitejs/plugin-react 6.1.2; both servers capped at 4 threads
- Input
- A scratch copy of private-app's apps/web with its dependencies installed
- Command
scripts/limit.sh --mem-mb 1024 --time-s 1800 -- node bench/private-app/dev.mjs --app /tmp/pp/apps/web --server both --mode bench- Recorded in
- commit
e71a460
ts-r-dev: hot reload latency as the app grows
Time from saving a file to the browser having re-imported the changed module and run its callbacks, on generated apps. The plan's target is under 30 ms at p50, staying flat as the project grows. Vite has not been measured on these generated apps yet.
- 4.9 ms at 1,000 modules
- 4.8–5.0 ms at 10,000 modules
- 5.4 ms at 50,000 modules
How we measured
- Measured
- Browser-side timing in headless Chrome: 47 measured edits per size after 3 warm-up edits. Ranges are across runs; the bar shows the upper end.
- Machine
- Apple M-series Mac, macOS arm64, release build (the exact chip was not recorded)
- Software
- ts-r-dev release build, headless Chrome
- Input
- Generated JavaScript apps of 1,000, 10,000 and 50,000 modules; one self-accepting module under a 100-wide import tree is edited. The first save of a file goes through FSEvents, later saves through the kqueue fast path.
- Command
TS_R_LATENCY_SIZES=1000,10000,50000 cargo test --release -p ts_r_dev --test latency -- --ignored --nocapture- Recorded in
- commit
9ed299f
Command line
An excerpt of ts-r-dev --help. You can also run it through the launcher as ts-r dev.
Next to Vite
Use ts-r-dev for the dev loop and keep Vite, or anything else, for production builds. It reads your index.html and package.json. The client runtime is served at both /@ts-r/client and /@vite/client and its messages match Vite’s, so framework code that listens to vite: events keeps working.
ts-r-dev: dev server with hot module replacement (replaces vite dev)
Usage: ts-r-dev [root] [options]
--port <n> Port to listen on (default 5173)
--host <addr> Address to bind (default 127.0.0.1)
--no-cache Keep transform results in memory only
--force Pre-bundle dependencies again
--no-react-refresh Turn off React Fast Refresh