Skip to content

Commit d853691

Browse files
committed
docs: document React Router desktop support
1 parent 13b0aa6 commit d853691

4 files changed

Lines changed: 96 additions & 17 deletions

File tree

runtime/desktop/frameworks.md

Lines changed: 78 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
2-
last_modified: 2026-06-25
2+
last_modified: 2026-06-27
33
title: "Frameworks"
4-
description: "Run Next.js, Astro, Fresh, Remix, Nuxt, SvelteKit, SolidStart, TanStack Start, and Vite projects as desktop apps with no code changes."
4+
description: "Run Next.js, Astro, Fresh, Remix, React Router, Nuxt, SvelteKit, SolidStart, TanStack Start, and Vite projects as desktop apps."
55
---
66

77
:::info Available in Deno 2.9
@@ -17,12 +17,13 @@ framework's production server (or dev server under `--hmr`) with the webview
1717
pointed at it.
1818

1919
```sh
20-
# Inside a Next.js / Astro / Fresh / etc. project:
20+
# Inside a Next.js / Astro / Fresh / React Router / etc. project:
2121
deno desktop .
2222
```
2323

24-
No code changes, no special adapter. The same project that runs as a web app
25-
ships as a desktop app.
24+
Most supported frameworks need no special adapter. Some frameworks have
25+
runtime-specific entry files; check the per-framework notes below before
26+
building.
2627

2728
## Detection
2829

@@ -35,6 +36,7 @@ match wins.
3536
| Astro | `astro.config.{mjs,ts,js}` |
3637
| Fresh | `fresh.gen.ts` or `_fresh/` directory |
3738
| Remix | `@remix-run/react` or `@remix-run/dev` in `package.json` |
39+
| React Router | `@react-router/dev` in `package.json` |
3840
| Nuxt | `nuxt.config.{ts,js,mjs}` |
3941
| SvelteKit | `svelte.config.{js,ts}` |
4042
| SolidStart | `@solidjs/start` in `package.json` |
@@ -112,6 +114,75 @@ deno desktop .
112114
Production: runs `remix-serve` against the `build/` directory. Dev (under
113115
`--hmr`): `@remix-run/dev` CLI.
114116

117+
### React Router
118+
119+
React Router framework mode is detected by `@react-router/dev` in
120+
`package.json`. Both SPA mode (`ssr: false`) and server-side rendering are
121+
supported once the project builds successfully with Deno.
122+
123+
React Router's default server entry targets Node.js and uses
124+
`renderToPipeableStream` from `react-dom/server`. Deno resolves
125+
`react-dom/server` to a Web Streams build that uses `renderToReadableStream`
126+
instead. Add a Deno-compatible `app/entry.server.tsx` before running
127+
`react-router build`; React Router also uses this file to prerender the SPA
128+
fallback when `ssr: false`.
129+
130+
```tsx title="app/entry.server.tsx"
131+
import type { EntryContext } from "react-router";
132+
import { ServerRouter } from "react-router";
133+
import { renderToReadableStream } from "react-dom/server";
134+
import { isbot } from "isbot";
135+
136+
export default async function handleRequest(
137+
request: Request,
138+
responseStatusCode: number,
139+
responseHeaders: Headers,
140+
routerContext: EntryContext,
141+
) {
142+
if (request.method.toUpperCase() === "HEAD") {
143+
return new Response(null, {
144+
status: responseStatusCode,
145+
headers: responseHeaders,
146+
});
147+
}
148+
149+
let statusCode = responseStatusCode;
150+
151+
const body = await renderToReadableStream(
152+
<ServerRouter context={routerContext} url={request.url} />,
153+
{
154+
signal: request.signal,
155+
onError(error: unknown) {
156+
statusCode = 500;
157+
console.error(error);
158+
},
159+
},
160+
);
161+
162+
if (
163+
isbot(request.headers.get("user-agent") || "") || routerContext.isSpaMode
164+
) {
165+
await body.allReady;
166+
}
167+
168+
responseHeaders.set("Content-Type", "text/html");
169+
return new Response(body, {
170+
headers: responseHeaders,
171+
status: statusCode,
172+
});
173+
}
174+
```
175+
176+
Then build and package the app:
177+
178+
```sh
179+
deno task build
180+
deno desktop .
181+
```
182+
183+
Production serves static client assets from `build/client` and, for SSR
184+
projects, routes requests through `build/server/index.js`.
185+
115186
### Nuxt
116187

117188
```sh
@@ -147,8 +218,8 @@ Both use the Nitro framework underneath; detection handles them via the
147218

148219
Vite projects are detected by a `vite.config.*` file or a `vite` dependency.
149220
This sits at the lowest bundler priority, so the meta-frameworks built on Vite
150-
(Astro, SvelteKit, Nuxt, Remix, SolidStart, TanStack Start) are matched by their
151-
own config or dependency first.
221+
(Astro, SvelteKit, Nuxt, Remix, React Router, SolidStart, TanStack Start) are
222+
matched by their own config or dependency first.
152223

153224
- **SSR** (a `server.{ts,js,mjs}` entry alongside `vite.config.*`): the SSR
154225
entry runs directly in production, and dev (under `--hmr`) runs the Vite dev

runtime/desktop/index.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
last_modified: 2026-06-25
2+
last_modified: 2026-06-27
33
title: "Desktop apps"
44
description: "Build self-contained desktop applications from a Deno project, with framework auto-detection, hot reload, native windowing, auto-update, and cross-platform distribution."
55
---
@@ -32,10 +32,10 @@ integration.
3232
the bundled Chromium (CEF) backend when you need identical rendering across
3333
macOS, Windows, and Linux.
3434
- **Framework auto-detection.** Point `deno desktop` at a Next.js, Astro, Fresh,
35-
Remix, Nuxt, SvelteKit, SolidStart, TanStack Start, or Vite SSR project and it
36-
runs: the production server in release mode, the dev server with hot reload
37-
under `--hmr`. No code changes are required to take an existing web project to
38-
the desktop.
35+
Remix, React Router, Nuxt, SvelteKit, SolidStart, TanStack Start, or Vite SSR
36+
project and it runs: the production server in release mode, the dev server
37+
with hot reload under `--hmr`. Most frameworks need no special adapter; see
38+
[Frameworks](/runtime/desktop/frameworks/) for per-framework requirements.
3939
- **In-process bindings instead of IPC.** Backend and UI communication goes
4040
through in-process channels, not socket-based IPC. Values are still encoded as
4141
they cross the call boundary, but there is no cross-process round-trip between

runtime/reference/cli/compile.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
last_modified: 2026-06-25
2+
last_modified: 2026-06-27
33
title: "deno compile"
44
oldUrl:
55
- /runtime/manual/tools/compile/
@@ -43,14 +43,15 @@ Supported frameworks:
4343
- Astro
4444
- Fresh (1.x and 2.x)
4545
- Remix
46+
- React Router
4647
- SvelteKit
4748
- Nuxt
4849
- SolidStart
4950
- TanStack Start
5051
- Vite (SSR, plus SPA/MPA projects served as static output)
5152

5253
```sh
53-
# In a Next.js / Astro / Fresh / etc. project
54+
# In a Next.js / Astro / Fresh / React Router / etc. project
5455
deno compile .
5556

5657
# Or pointing at a specific app directory
@@ -61,6 +62,12 @@ Generated entrypoints use `import.meta.dirname` so framework asset paths resolve
6162
correctly against the [virtual filesystem](#including-data-files-or-directories)
6263
inside the compiled binary.
6364

65+
React Router projects must build successfully under Deno before they can be
66+
compiled. Because React Router's default server entry targets Node.js, add a Web
67+
Streams-compatible `app/entry.server.tsx` when building with Deno. See the
68+
[React Router desktop framework notes](/runtime/desktop/frameworks/#react-router)
69+
for an example.
70+
6471
If the project doesn't match any supported framework, `deno compile` will error
6572
out.
6673

runtime/reference/cli/desktop.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,10 @@ deno desktop --output MyApp.app main.ts
2424
```
2525

2626
The entrypoint is optional. A bare `deno desktop` (or `deno desktop .`) detects
27-
a supported framework (Next.js, Astro, Fresh, and others) in the current
28-
directory and builds it without any code changes. See
29-
[Frameworks](/runtime/desktop/frameworks/).
27+
a supported framework (Next.js, Astro, Fresh, React Router, and others) in the
28+
current directory and builds the appropriate desktop entrypoint. See
29+
[Frameworks](/runtime/desktop/frameworks/) for supported frameworks and
30+
per-framework requirements.
3031

3132
This page covers the command-line flags. For the full guide (backends,
3233
[`Deno.BrowserWindow`](/api/deno/~/Deno.BrowserWindow), bindings, auto-update,

0 commit comments

Comments
 (0)