Skip to content

Repository files navigation

@webviewjs/webview

CInpmLicense

Build lightweight native desktop applications with JavaScript and the webview engine already provided by the operating system. WebviewJS is a typed N-API binding to tao and wry, with a small JavaScript surface and first-class support for Node.js, Bun, and Deno.

It is a good fit for utilities, internal tools, desktop companions, and existing web applications that need a native window without bundling a second browser engine.

Get started · API reference · Examples · Platform notes

WebviewJS preview

Highlights

  • Native windows backed by WebView2, WebKit, or WebKitGTK instead of a bundled browser runtime.
  • Non-blocking event pumping that keeps ordinary JavaScript timers and I/O responsive.
  • Typed APIs for windows, webviews, menus, dialogs, cookies, DevTools, and window controls.
  • Shared browser contexts for profiles, cookies, cache, storage, and automation.
  • System tray icons, native notifications, and platform-specific window extensions.
  • IPC through window.ipc.postMessage() plus Promise-based webview.expose() namespaces.
  • Fetch-compatible asynchronous custom protocols, including Hono routing without an HTTP server.
  • A CLI for compiling Node.js, Deno, or Bun applications into standalone executables.

Note

WebviewJS provides the native window and webview layer. It is intentionally smaller than an application framework such as Electron or Tauri, so you can choose your own frontend, routing, state management, and build tools.

Documentation

The complete documentation is available at webview.js.org. For an index designed for documentation tools and assistants, see llms.txt.

Getting started

InstallationSystem requirements and setup
Quick StartYour first window in minutes
Event LoopHow the non-blocking pump works

API reference

ApplicationRoot object, event loop, windows, menus
BrowserWindowOS window, size, position, cursor, decorations
WebviewEmbedded browser, navigation, cookies, script, bounds
WebContextShared browser data, profiles, and automation
System TrayTray icons, menus, updates, and pointer events
NotificationNative desktop notifications and lifecycle events
MenuNative menu bar construction
TypesShared interfaces and enums

Guides

Building ExecutablesCompile to .exe / binary with node, deno, bun
IPC MessagingPage ↔ Node communication
MenusBuilding menu bars with roles and accelerators
Multiple WindowsManaging several windows
Cookies & StorageReading, writing, and clearing cookies
Custom ProtocolsServing local content to the webview

Platform notes

WindowsWebView2, taskbar, DPI
macOSWebKit, main-thread requirement, app menu
LinuxWebKitGTK, Wayland/X11, menu limitations
iOSOrientation, status bar, and gestures
AndroidContent rectangle and configuration

Installation

Install the package with your preferred JavaScript package manager. The native platform package is resolved automatically through optional dependencies.

npm install @webviewjs/webview
# or
bun add @webviewjs/webview
# or
pnpm add @webviewjs/webview

Keep optional dependencies enabled when installing. They contain the native addon selected for the current operating system and architecture.

System requirements

PlatformRequirements
WindowsWebView2. It ships with Windows 11 and current Edge installations; Windows 10 can install it automatically.
macOSmacOS 10.15 Catalina or later. WebKit is built in.
LinuxWebKitGTK 4.1 and libxdo. See the Linux platform guide.
Android and iOSNative project setup and platform SDKs. See the hosted documentation.

For distribution and platform-specific behavior, review the complete installation guide before shipping.

Supported platforms

TargetPlatformArchitectureStatusNotes
x86_64-pc-windows-msvcWindowsx64SupportedWebView2
i686-pc-windows-msvcWindowsx86SupportedWebView2
aarch64-pc-windows-msvcWindowsarm64SupportedWebView2
x86_64-apple-darwinmacOSx64SupportedWebKit
aarch64-apple-darwinmacOSarm64SupportedWebKit
x86_64-unknown-linux-gnuLinuxx64SupportedWebKitGTK 4.1, X11, and Wayland
i686-unknown-linux-gnuLinuxx86SupportedWebKitGTK 4.1, X11, and Wayland
aarch64-unknown-linux-gnuLinuxarm64SupportedWebKitGTK 4.1, X11, and Wayland
armv7-unknown-linux-gnueabihfLinuxarmv7SupportedWebKitGTK 4.1, X11, and Wayland
aarch64-linux-androidAndroidarm64ExperimentalPlatform APIs are still evolving
armv7-linux-androideabiAndroidarmv7ExperimentalPlatform APIs are still evolving
x86_64-unknown-freebsdFreeBSDx64StubPackage resolution only; no GUI implementation

Examples

Quick start

import{Application}from'@webviewjs/webview';constapp=newApplication();constwindow=app.createBrowserWindow({title: 'WebviewJS',width: 1024,height: 768,});window.createWebview({url: 'https://example.com'});app.run();

For CommonJS projects, use the same API through require():

const{ Application }=require('@webviewjs/webview');constapp=newApplication();constwindow=app.createBrowserWindow({title: 'WebviewJS'});window.createWebview({html: '<h1>Hello from WebviewJS</h1>'});app.run();

The application owns native resources created through it. Call app.exit() when your application needs to shut down explicitly:

process.on('SIGINT',()=>{app.exit();});

Event pumping

app.whenReady() starts the non-blocking event pump by default:

awaitapp.whenReady({interval: 16,ref: true});

For manual startup, disable auto-run:

constready=app.whenReady({autoRun: false});app.run({interval: 16,ref: true});awaitready;

interval defaults to 16 milliseconds and ref defaults to true. Use app.pumpEvents() for manual pumping.

System tray

Keep a strong JavaScript reference when you need to call tray methods or keep its listeners reachable:

lettray=null;app.whenReady().then(()=>{tray=app.createTrayIcon({id: 'main',icon: {data: rgba,width: 16,height: 16},tooltip: 'My application',menu: {items: [{id: 'quit',label: 'Quit'}]},});tray.on('click',(event)=>console.log(event));});

See the system tray reference and runnable tray example.

Notifications

import{Notification}from'@webviewjs/webview';constnotification=newNotification('Build complete',{body: 'The release executable is ready.',});notification.on('click',()=>console.log('notification clicked'));notification.on('error',({ error })=>console.error(error));

Notification permission is always "granted" for native applications. See the notification reference and runnable notification example.

IPC and exposed functions

The webview page can send messages to Node through window.ipc.postMessage():

constwebview=window.createWebview({ipcName: 'bindings'});webview.onIpcMessage((message)=>console.log(message.body.toString()));

ipcName adds an alias, so the page can use window.bindings.postMessage(...); window.ipc remains available.

For typed request/response style calls, expose a namespace:

webview.expose('native',{version: '0.1.4',readConfig: async()=>JSON.parse(awaitreadFile('./config.json','utf8')),});

In the page:

console.log(window.native.version);constconfig=awaitwindow.native.readConfig();

Every exposed function returns a Promise in the page. Values, arguments, and results must be JSON-serializable. Violations use SerializationError.

Asynchronous custom protocols

Register a protocol before creating its webview:

window.registerProtocol('app',async(request)=>{constfilePath=join(process.cwd(),'dist',newURL(request.url).pathname);try{returnnewResponse(awaitreadFile(filePath),{headers: {'Content-Type': 'text/html; charset=utf-8'},});}catch{returnnewResponse('Not found',{status: 404,headers: {'Content-Type': 'text/plain; charset=utf-8'},});}});window.createWebview({url: 'app://localhost/index.html'});

See Custom Protocols, IPC, and the runnable custom protocol and expose examples.

Menu system

WebviewJS provides a cross-platform menu system that works on macOS, Windows, and Linux.

Basic menu setup

import{Application}from'@webviewjs/webview';constapp=newApplication();// Set global application menuapp.setMenu({items: [{label: 'File',submenu: {items: [{id: 'new',label: 'New',accelerator: 'CmdOrCtrl+N'},{id: 'open',label: 'Open',accelerator: 'CmdOrCtrl+O'},{role: 'separator'},{id: 'quit',label: 'Quit',accelerator: 'CmdOrCtrl+Q'},],},},{label: 'Edit',submenu: {items: [{role: 'copy'},{role: 'paste'},{role: 'cut'},{role: 'selectall'}],},},],});constwindow=app.createBrowserWindow();constwebview=window.createWebview({url: 'https://nodejs.org'});app.run();

Menu event handling

import{Application}from'@webviewjs/webview';constapp=newApplication();// Handle menu eventsapp.on('custom-menu-click',({customMenuEvent: menuEvent})=>{console.log(`Menu item clicked: ${menuEvent.id}`);console.log(`From window: ${menuEvent.windowId}`);// Handle specific menu itemsswitch(menuEvent.id){case'new':
console.log('Creating new document...');break;case'open':
console.log('Opening file...');break;case'quit':
app.exit();break;}});// Set up menu...app.setMenu({/* ... */});

Window-specific menus

constapp=newApplication();// Create window with custom menuconstwindow=app.createBrowserWindow({title: 'Custom Window',menu: {items: [{id: 'window-action',label: 'Window Action',accelerator: 'Ctrl+W',},],},});// Or check if window has a menuif(window.hasMenu()){console.log('This window has a menu');}

Menu item options

  • id: Unique identifier for the menu item (used in events)
  • label: Display text for the menu item
  • enabled: Whether the item is clickable (default: true)
  • accelerator: Keyboard shortcut (e.g., "CmdOrCtrl+N", "Alt+F4")
  • submenu: Nested menu items
  • role: Predefined menu items with built-in behavior

Predefined menu roles

  • "copy": Standard copy action
  • "paste": Standard paste action
  • "cut": Standard cut action
  • "selectall": Select all text action
  • "separator": Visual separator line

IPC

constapp=newApplication();constwindow=app.createBrowserWindow();constwebview=window.createWebview({html: `<!DOCTYPE html> <html> <head> <title>Webview</title> </head> <body> <h1 id="output">Hello world!</h1> <button id="btn">Click me!</button> <script> btn.onclick = function send() { window.ipc.postMessage('Hello from webview'); } </script> </body> </html> `,preload: `window.onIpcMessage = function(data) { const output = document.getElementById('output'); output.innerText = \`Server Sent A Message: \${data}\`; }`,});if(!webview.isDevtoolsOpen())webview.openDevtools();webview.onIpcMessage((data)=>{constreply=`You sent ${data.body.toString('utf-8')}`;webview.evaluateScript(`onIpcMessage("${reply}")`);});app.run();

Closing the application

You can close the application, windows, and webviews gracefully to ensure all resources (including temporary folders) are cleaned up properly.

constapp=newApplication();constwindow=app.createBrowserWindow();constwebview=window.createWebview({url: 'https://nodejs.org'});app.on('application-close-requested',()=>{console.log('Application is closing, cleaning up resources...');});app.on('window-close-requested',()=>{console.log('Window close requested');});// Close the application gracefully (cleans up temp folders)app.exit();// Or hide/show the windowwindow.hide();// Hide the windowwindow.show();// Show the window again// Or reload the webviewwebview.reload();

For more details on application lifecycle and disposal, see the Application API reference and the closing example.

Keep strong references

Retain BrowserWindow, Webview, WebContext, and TrayIcon wrappers for as long as you need to call their methods or retain their JavaScript listeners. Avoid discarded temporary handles:

constwindows=[];app.whenReady().then(()=>{constwindow=app.createBrowserWindow();constwebview=window.createWebview({url: 'https://example.com'});windows.push({ window, webview });});

The root Application owns native resources created through it. app.exit(), app[Symbol.dispose](), and application garbage collection dispose those resources in shutdown order. Retained wrappers then report isDisposed() === true, and method calls fail with a disposed error. Individual windows, webviews, contexts, and tray icons also support dispose() and Symbol.dispose.

Check out examples directory for more examples:

Run any example with: node examples/menu-system.mjs (after building the project)

Building executables

The webview CLI creates a standalone executable by delegating to the selected runtime's compiler:

  • Node.js uses Node's Single Executable Application (SEA) workflow.
  • Deno uses deno compile.
  • Bun uses bun build --compile.

It is not a desktop application installer builder, package manager, or full application bundler. It does not create platform installers or perform cross-compilation. The WebviewJS .node addon is platform-specific, so build on the target operating system and architecture, or prepare the matching native addon and runtime toolchain manually before packaging. Use platform distribution tools alongside this CLI for installers, release metadata, signing, and notarization.

Note

The CLI is evolving. Review the runtime and platform requirements before distributing an executable.

The webview CLI compiles your app into a single self-contained executable. The runtime is auto-detected (Bun → bun, Deno → deno, otherwise Node.js), or you can override it:

# Auto-detected runtime
webview --build --input ./path/to/your/script.js --output ./dist --name my-app
# Explicit runtime
webview --build --runtime node --input ./src/index.js --name my-app
webview --build --runtime deno --input ./src/index.ts --name my-app
webview --build --runtime bun --input ./src/index.ts --name my-app
FlagDefaultDescription
--runtime / -Rauto-detectednode, deno, or bun
--input / -i./index.jsEntry file
--output / -o./distOutput directory
--name / -nwebviewjsExecutable name
--resources / -rnoneJSON asset map (Node.js only)

For runtime-specific details, asset embedding, code signing, and release guidance, see Building Executables.

Agent skill

This repository includes a reusable WebviewJS skill for coding agents. Install it with:

npx skills add webviewjs/webview

It covers application structure, native prerequisites, platform constraints, API patterns, and executable builds.

Development

Prerequisites

Setup

bun install

Build

bun run build

About

Robust cross-platform webview library for Node/Deno/Bun

Topics

Resources

Stars

62 stars

Watchers

5 watching

Forks

Releases

Packages

Used by

Contributors

Languages