Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 78 additions & 7 deletions runtime/desktop/frameworks.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
last_modified: 2026-06-25
last_modified: 2026-06-27
title: "Frameworks"
description: "Run Next.js, Astro, Fresh, Remix, Nuxt, SvelteKit, SolidStart, TanStack Start, and Vite projects as desktop apps with no code changes."
description: "Run Next.js, Astro, Fresh, Remix, React Router, Nuxt, SvelteKit, SolidStart, TanStack Start, and Vite projects as desktop apps."
---

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

```sh
# Inside a Next.js / Astro / Fresh / etc. project:
# Inside a Next.js / Astro / Fresh / React Router / etc. project:
deno desktop .
```

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

## Detection

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

### React Router

React Router framework mode is detected by `@react-router/dev` in
`package.json`. Both SPA mode (`ssr: false`) and server-side rendering are
supported once the project builds successfully with Deno.

React Router's default server entry targets Node.js and uses
`renderToPipeableStream` from `react-dom/server`. Deno resolves
`react-dom/server` to a Web Streams build that uses `renderToReadableStream`
instead. Add a Deno-compatible `app/entry.server.tsx` before running
`react-router build`; React Router also uses this file to prerender the SPA
fallback when `ssr: false`.

```tsx title="app/entry.server.tsx"
import type { EntryContext } from "react-router";
import { ServerRouter } from "react-router";
import { renderToReadableStream } from "react-dom/server";
import { isbot } from "isbot";

export default async function handleRequest(
request: Request,
responseStatusCode: number,
responseHeaders: Headers,
routerContext: EntryContext,
) {
if (request.method.toUpperCase() === "HEAD") {
return new Response(null, {
status: responseStatusCode,
headers: responseHeaders,
});
}

let statusCode = responseStatusCode;

const body = await renderToReadableStream(
<ServerRouter context={routerContext} url={request.url} />,
{
signal: request.signal,
onError(error: unknown) {
statusCode = 500;
console.error(error);
},
},
);

if (
isbot(request.headers.get("user-agent") || "") || routerContext.isSpaMode
) {
await body.allReady;
}

responseHeaders.set("Content-Type", "text/html");
return new Response(body, {
headers: responseHeaders,
status: statusCode,
});
}
```

Then build and package the app:

```sh
deno task build
deno desktop .
```

Production serves static client assets from `build/client` and, for SSR
projects, routes requests through `build/server/index.js`.

### Nuxt

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

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

- **SSR** (a `server.{ts,js,mjs}` entry alongside `vite.config.*`): the SSR
entry runs directly in production, and dev (under `--hmr`) runs the Vite dev
Expand Down
10 changes: 5 additions & 5 deletions runtime/desktop/index.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
last_modified: 2026-06-25
last_modified: 2026-06-27
title: "Desktop apps"
description: "Build self-contained desktop applications from a Deno project, with framework auto-detection, hot reload, native windowing, auto-update, and cross-platform distribution."
---
Expand Down Expand Up @@ -32,10 +32,10 @@ integration.
the bundled Chromium (CEF) backend when you need identical rendering across
macOS, Windows, and Linux.
- **Framework auto-detection.** Point `deno desktop` at a Next.js, Astro, Fresh,
Remix, Nuxt, SvelteKit, SolidStart, TanStack Start, or Vite SSR project and it
runs: the production server in release mode, the dev server with hot reload
under `--hmr`. No code changes are required to take an existing web project to
the desktop.
Remix, React Router, Nuxt, SvelteKit, SolidStart, TanStack Start, or Vite SSR
project and it runs: the production server in release mode, the dev server
with hot reload under `--hmr`. Most frameworks need no special adapter; see
[Frameworks](/runtime/desktop/frameworks/) for per-framework requirements.
- **In-process bindings instead of IPC.** Backend and UI communication goes
through in-process channels, not socket-based IPC. Values are still encoded as
they cross the call boundary, but there is no cross-process round-trip between
Expand Down
11 changes: 9 additions & 2 deletions runtime/reference/cli/compile.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
last_modified: 2026-06-25
last_modified: 2026-06-27
title: "deno compile"
oldUrl:
- /runtime/manual/tools/compile/
Expand Down Expand Up @@ -43,14 +43,15 @@ Supported frameworks:
- Astro
- Fresh (1.x and 2.x)
- Remix
- React Router
- SvelteKit
- Nuxt
- SolidStart
- TanStack Start
- Vite (SSR, plus SPA/MPA projects served as static output)

```sh
# In a Next.js / Astro / Fresh / etc. project
# In a Next.js / Astro / Fresh / React Router / etc. project
deno compile .

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

React Router projects must build successfully under Deno before they can be
compiled. Because React Router's default server entry targets Node.js, add a Web
Streams-compatible `app/entry.server.tsx` when building with Deno. See the
[React Router desktop framework notes](/runtime/desktop/frameworks/#react-router)
for an example.

If the project doesn't match any supported framework, `deno compile` will error
out.

Expand Down
9 changes: 5 additions & 4 deletions runtime/reference/cli/desktop.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
last_modified: 2026-06-25
last_modified: 2026-06-27
title: "deno desktop"
openGraphLayout: "/open_graph/cli-commands.jsx"
openGraphTitle: "deno desktop"
Expand All @@ -24,9 +24,10 @@ deno desktop --output MyApp.app main.ts
```

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

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