windowd
v0.7.1
Published
Wrap any Vite project in an NW.js shell with full renderer Node.js access
Maintainers
Readme

windowd
Stupidly simple desktop apps. All you need is an index.html then npx windowd to get a desktop app with Hot Module Reloading.
Quick start
mkdir my-app && cd my-appCreate an index.html:
<!DOCTYPE html>
<html>
<body>
<h1>Hello from the desktop</h1>
<pre id="info"></pre>
<script type="module">
import os from 'node:os';
document.getElementById('info').textContent =
`Running on ${os.platform()} with Node ${process.version}`;
</script>
</body>
</html>Run it:
npx windowdThat's it. You get a desktop window with Vite HMR and full Node.js APIs available in your code.
What you get
- Single command to run any
index.htmlor Vite project as a desktop app - Full Node.js APIs in the renderer -
node:fs,node:child_process,process,require, all of it - Vite dev server + HMR - instant hot reload as you edit
- Zero config - no
package.json, no build setup, no Vite config needed to start - Auto-scaffolding - run
npx windowdin an empty folder and pick a template - TypeScript ready - auto-generates
tsconfig.jsonand installs NW.js types when TypeScript files are detected
Using Node.js in the renderer
Yeah, this isn't the safest thing in the world but so long as you're running your own trusted code then YOLO 🤷♂️ What it does get you is all the power of a desktop app and all the power of a web app, all in one. Read files, spawn processes, grab system info, then render it all with React:
import { readFileSync } from 'node:fs';
import os from 'node:os';
import React from 'react';
import { createRoot } from 'react-dom/client';
const pkg = JSON.parse(readFileSync('./package.json', 'utf-8'));
const cpus = os.cpus().length;
const mem = Math.round(os.totalmem() / 1024 / 1024 / 1024);
function App() {
return (
<div>
<h1>{pkg.name} v{pkg.version}</h1>
<p>{cpus} cores, {mem} GB RAM, {os.platform()}</p>
</div>
);
}
createRoot(document.getElementById('root')!).render(<App />);⚠️ Warning ⚠️ any script running in your app can read/write files, run shell commands, and access the network with full OS-level permissions. Only run code you trust.
Scaffolding a new project
Run npx windowd in an empty directory (no index.html or Vite config) and you'll get an interactive prompt:
? What would you like to do?
> Scaffold a React + TypeScript project here
Scaffold a vanilla HTML + TypeScript project
ExitPick a template and windowd will scaffold it, install dependencies, and launch the app immediately.
CLI options
npx windowd # run in current directory
npx windowd --width 1440 --height 900 # custom window size
npx windowd --title "My App" # custom window title
npx windowd --debug # extra NW.js logging
npx windowd --artifacts .windowd/logs # write CLI + app logs
npx windowd --capture .windowd/run1 # screenshot + result.json, then exit
npx windowd --init # create/validate tsconfig.json
npx windowd --version
npx windowd --helpTypeScript
windowd auto-detects TypeScript and handles setup for you:
- tsconfig.json generated automatically when
.ts/.tsxfiles are present and no tsconfig exists - @types/nw.js installed automatically for NW.js API autocomplete (
nw.Window,nw.Menu, etc.) - Vite + React plugin auto-injected for
.tsx/.jsxprojects without their own Vite config
Run npx windowd --init to validate an existing tsconfig.json and see which recommended settings are missing.
Configuration
Create a windowd-config.ts (or .js, .mjs, .cjs) in your project root to customize NW.js behavior:
export default {
nw: {
window: {
frame: false,
always_on_top: true,
},
chromiumArgs: "--disable-background-timer-throttling",
nodeRemote: ["<all_urls>"],
},
};You can also set nw.manifest to add extra NW.js manifest fields. Core runtime keys (name, main, node-main) are protected and cannot be overridden.
Window title and icon
windowd picks these up automatically so the window feels native without extra configuration.
Title resolution order:
--titleCLI flagwindowd.titleinpackage.jsondisplayNameinpackage.jsonnameinpackage.json<title>tag inindex.html- Directory name as fallback
Icon resolution order:
window.iconinwindowd-config.ts<link rel="icon">inindex.html(PNG, ICO, JPG supported, SVG is skipped)favicon.ico/favicon.png/favicon.jpgin project root orpublic/- Built-in default icon (application_xp from famfamfam-silk)
DevTools
- Right-click anywhere to open from the context menu
F12orCtrl+Shift+I(Cmd+Shift+Ion macOS)
DevTools require the NW.js SDK build, which windowd installs by default.
Debugging for agents
If you are an agent and you are trying to debug a flaky app/test, run with artifacts enabled first.
# From a project root
npx windowd --artifacts .windowd/debugThis writes:
.windowd/debug/cli.log- everything from the CLI + child processes (Vite/NW stderr/stdout).windowd/debug/app.log- console output from inside the app window (console.log,console.error, uncaught errors, unhandled rejections)
app.log is captured via NW.js inject_js_start so it hooks console before your app scripts run.
When --artifacts is enabled, any custom nw.manifest.inject_js_start is temporarily overridden by windowd's logger preload.
For E2E style debugging (capture screenshot and exit):
npx windowd --capture .windowd/run1 --artifacts .windowd/run1This also writes:
.windowd/run1/screenshot.png.windowd/run1/result.json
If something fails, inspect cli.log first, then app.log, then check the screenshot.
Requirements
- Node.js >= 18 (or Bun >= 1.0)
- ~200 MB disk space for the NW.js runtime, downloaded automatically on first run
How it works
windowdstarts a Vite dev server on an ephemeral port- A temporary NW.js host app is generated with Node integration enabled
- NW.js opens your app URL with full
node-remoteaccess - When the window closes, Vite and the host app are cleaned up automatically
Example apps
The repo includes test apps you can run to see different features in action:
git clone https://github.com/mikecann/windowd.git
cd windowd
bun install
bun run test-app <name>| Name | Command | What it demonstrates |
|---|---|---|
| basics | bun run test-app basics | React + TypeScript, node:fs directory listing, Vite asset imports, NW.js window APIs (minimize, maximize, always-on-top, child windows) |
| config | bun run test-app config | Frameless window via windowd-config.ts, shows how to customize NW.js settings |
| deps | bun run test-app deps | Third-party npm package (canvas-confetti) alongside Node.js APIs, has its own package.json |
| justhtml | bun run test-app justhtml | Plain HTML with zero config, no TypeScript, no package.json, HMR still works |
