npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@nativewindow/webview

v1.0.1

Published

Native OS webview windows for Bun & Node.js (beta)

Readme

@nativewindow/webview

CI npm npm npm npm

[!NOTE] This project is in beta. APIs may change without notice and some features may be incomplete or unstable.

Native OS webviews for Bun, Deno & Node.js. Create real desktop windows with embedded web content using wry + tao — providing WebKit on macOS and Linux, and WebView2 on Windows.

Features

  • Native webviews — powered by wry + tao (WebKit on macOS/Linux, WebView2 on Windows), no Electron or Chromium bundled
  • Multi-window — create and manage multiple independent windows
  • HTML & URL loading — load inline HTML strings or navigate to URLs
  • Bidirectional IPC — send messages between Bun/Deno/Node and the webview
  • Typesafe IPC channels — typed message layer with schema-based validation and compile-time checked event maps
  • Full window control — title, size, position, min/max size, decorations, transparency, always-on-top
  • Window events — close, resize, move, focus, blur, page load, title change
  • Rust + napi-rs + wry + tao — high-performance native addon, no runtime overhead
  • Runtime detection — check for WebView2 availability and auto-install on Windows

Packages

| Package | Description | | ----------------------------------------------------------- | --------------------------------------------------------- | | @nativewindow/webview | Rust napi-rs addon providing native window + webview APIs | | @nativewindow/ipc | Pure TypeScript typesafe IPC channel layer | | @nativewindow/react | React bindings for the typed IPC layer | | @nativewindow/tsdb | TanStack DB collection adapter for native-window IPC |

Quick Start

import { init, pumpEvents, NativeWindow } from "native-window";

init();
const pump = setInterval(() => pumpEvents(), 16);

const win = new NativeWindow({
  title: "My App",
  width: 800,
  height: 600,
});

win.loadHtml(`
  <h1>Hello from native webview!</h1>
  <button onclick="window.ipc.postMessage('clicked')">Click me</button>
`);

win.onMessage((msg) => {
  console.log("From webview:", msg);
  win.postMessage(`Echo: ${msg}`);
});

win.onClose(() => {
  clearInterval(pump);
  process.exit(0);
});

Typed IPC

Use native-window-ipc for compile-time checked messaging between Bun/Deno/Node and the webview. Schemas provide both types and runtime validation.

Host side (Bun/Deno/Node)

import { z } from "zod";
import { createWindow } from "@nativewindow/ipc";

const ch = createWindow(
  { title: "Typed IPC" },
  {
    schemas: {
      // host -> webview
      host: {
        "update-title": z.string(),
      },
      // webview -> host
      client: {
        "user-click": z.object({ x: z.number(), y: z.number() }),
        counter: z.number(),
      },
    },
  },
);

// Receive typed messages from the webview (client events)
ch.on("user-click", (pos) => {
  // pos: { x: number; y: number }
  console.log(`Click at ${pos.x}, ${pos.y}`);
});

ch.on("counter", (n) => {
  // n: number
  ch.send("update-title", `Count: ${n}`);
});

// ch.send("counter", "wrong");      // Type error: "counter" is a client event
// ch.send("typo", 123);             // Type error: "typo" does not exist

ch.window.loadHtml(`<html>...</html>`);

Webview side (inline HTML)

The __channel__ object is auto-injected by createWindow / createChannel:

<script>
  __channel__.send("user-click", { x: 10, y: 20 });
  __channel__.on("update-title", (title) => {
    document.title = title;
  });
</script>

Webview side (bundled app)

For webview apps bundled with their own build step, import the client directly:

import { z } from "zod";
import { createChannelClient } from "@nativewindow/ipc/client";

const ch = createChannelClient({
  schemas: {
    host: {
      "update-title": z.string(),
    },
    client: {
      counter: z.number(),
    },
  },
});

// Send client events to the host
ch.send("counter", 42); // Typed!

// Receive host events from the host
ch.on("update-title", (t) => {
  // t: string
  document.title = t;
});

Runtime Detection

On Windows 10, the WebView2 runtime may not be installed. Use checkRuntime() to detect it and ensureRuntime() to auto-install if missing.

import { checkRuntime, ensureRuntime } from "native-window";

const info = checkRuntime();
console.log(info);
// { available: true, version: "128.0.2739.42", platform: "windows" }
// { available: false, version: undefined, platform: "windows" }
// { available: true,  version: undefined,        platform: "macos" }
// { available: true,  version: undefined,        platform: "linux" }

if (!info.available) {
  console.log("WebView2 not found, installing...");
  const result = ensureRuntime(); // downloads ~2MB bootstrapper, runs silently
  console.log("Installed:", result.version);
}

On macOS, both functions return { available: true } immediately — WKWebView is a system framework. On Linux, both functions also return { available: true } — WebKitGTK is assumed to be installed. On Windows 11, WebView2 is pre-installed.

API Reference

native-window

init()

Initialize the native window system. Must be called once before creating any windows.

pumpEvents()

Process pending native UI events. Call periodically (~16ms via setInterval) to keep windows responsive.

checkRuntime(): RuntimeInfo

Check if the native webview runtime is available. Returns { available: boolean, version?: string, platform: "macos" | "windows" | "linux" | "unsupported" }.

ensureRuntime(): RuntimeInfo

Check for the runtime and install it if missing (Windows only). Downloads the WebView2 Evergreen Bootstrapper (~2MB) from Microsoft and runs it silently. Throws on failure.

new NativeWindow(options?)

Create a native window with an embedded webview.

WindowOptions:

| Option | Type | Default | Description | | ------------------------ | --------- | ------- | ----------------------------- | | title | string | "" | Window title | | width | number | 800 | Inner width (logical pixels) | | height | number | 600 | Inner height (logical pixels) | | x | number | — | X position | | y | number | — | Y position | | minWidth / minHeight | number | — | Minimum size | | maxWidth / maxHeight | number | — | Maximum size | | resizable | boolean | true | Allow resizing | | decorations | boolean | true | Show title bar and borders | | transparent | boolean | false | Transparent background | | alwaysOnTop | boolean | false | Float above other windows | | visible | boolean | true | Initially visible | | devtools | boolean | false | Enable devtools |

Content methods:

| Method | Description | | --------------------------- | ------------------------------------------------------------ | | loadUrl(url) | Navigate to a URL | | loadHtml(html) | Load an HTML string | | unsafe.evaluateJs(script) | Execute JS in the webview (fire-and-forget) | | postMessage(msg) | Send a string to the webview via window.__native_message__ |

Window control:

| Method | Description | | -------------------------------------------- | ---------------------------- | | setTitle(title) | Set the window title | | setSize(w, h) | Set the window size | | setMinSize(w, h) / setMaxSize(w, h) | Set size constraints | | setPosition(x, y) | Set window position | | setResizable(bool) | Toggle resizability | | setDecorations(bool) | Toggle decorations | | setAlwaysOnTop(bool) | Toggle always-on-top | | show() / hide() | Show or hide the window | | close() | Close and destroy the window | | focus() | Bring the window to focus | | maximize() / minimize() / unmaximize() | Window state |

Events:

| Method | Callback signature | | ---------------------------- | ------------------------------------------------------- | | onMessage(cb) | (message: string) => void | | onClose(cb) | () => void | | onResize(cb) | (width: number, height: number) => void | | onMove(cb) | (x: number, y: number) => void | | onFocus(cb) / onBlur(cb) | () => void | | onPageLoad(cb) | (event: "started" \| "finished", url: string) => void | | onTitleChanged(cb) | (title: string) => void |

native-window-ipc

createChannel<S>(win, options): NativeWindowChannel<InferSchemaMap<S>>

Wrap an existing NativeWindow with a typed message channel. Schemas are required. Auto-injects the webview client script (disable with { injectClient: false }).

createWindow<S>(windowOptions, channelOptions): NativeWindowChannel<InferSchemaMap<S>>

Convenience: creates a NativeWindow and wraps it with createChannel.

getClientScript(): string

Returns the webview-side client as a self-contained JS string for manual injection.

createChannelClient<S>(options): TypedChannel<InferSchemaMap<S>> (from native-window-ipc/client)

Create a typed channel client inside the webview. Schemas are required. For use in bundled webview apps.

TypedChannel<T>

interface TypedChannel<T extends EventMap> {
  send<K extends keyof T & string>(type: K, payload: T[K]): void;
  on<K extends keyof T & string>(type: K, handler: (payload: T[K]) => void): void;
  off<K extends keyof T & string>(type: K, handler: (payload: T[K]) => void): void;
}

Security

All security hardening is compiled in by default on all supported platforms — no build-time feature flags required.

  • URL scheme blockingjavascript:, file:, data:, and blob: navigations are blocked at the native layer
  • Content Security Policy — inject a CSP via the csp option in WindowOptions
  • Trusted origin filtering — restrict IPC messages and client injection to specific origins at the native and IPC layers
  • Webview surface hardening — context menus, status bar, and built-in error page are disabled on Windows
  • IPC bridge hardeningwindow.ipc and window.__channel__ are frozen, non-writable objects
  • Message size limits — 10 MB hard limit at the native layer, configurable 1 MB default at the IPC layer
  • Schema-based validation — all incoming IPC payloads are validated at runtime against user-defined schemas

See the Security documentation for the full threat model and best practices.

Building

Prerequisites

  • Bun (v1.3+), Deno (v2+), or Node.js (v18+)
  • Rust (stable)
  • macOS, Windows, or Linux (for native compilation)
  • On Linux: WebKitGTK development headers (e.g. libwebkit2gtk-4.1-dev on Ubuntu/Debian)

Install dependencies

bun install
# or
deno install

Build the native addon

cd packages/webview
bun run build          # release build
bun run build:debug    # debug build

The build targets the current platform. Cross-compilation targets are configured in packages/webview/package.json under napi.triples.

Samples

# Raw IPC example
bun samples/basic.ts

# Typed IPC example
bun samples/typed-ipc.ts

Testing

# Run the IPC channel tests
cd packages/ipc
bun test

Known Limitations

  • ~16ms event latency from the pumpEvents() polling interval
  • HTML null origin — content loaded via loadHtml() has a null CORS origin; use a custom protocol or loadUrl() for fetch/XHR
  • Windows 10 may require the WebView2 Runtime — use ensureRuntime() to auto-install (included by default on Windows 11)
  • Linux requires WebKitGTK to be installed (e.g. libwebkit2gtk-4.1-dev on Ubuntu/Debian)
  • No return values from unsafe.evaluateJs() — use postMessage/onMessage to send results back
  • 2 MB HTML limit on Windows when using loadHtml()
  • Use bun --watch instead of bun --hot for development (native addon reloading requires a process restart)

License

MIT