Native webview for JavaScript runtimes.
What is @rcompat/webview?#
A cross-runtime module for creating native desktop applications with web technologies. Uses the system's native webview (WebKit on macOS/Linux, WebView2 on Windows) for lightweight, fast rendering. Works with Bun and Deno.
Note: Node.js is unsupported; constructing a Webview there throws.
Installation#
Linux system requirements#
The Linux x64 sidecar uses WebKitGTK 6.0, GTK 4, and libsoup 3. Install the runtime packages supplied by your distribution before starting a webview:
# Arch Linux
sudo pacman -S webkitgtk-6.0
# Debian and Ubuntu
sudo apt install libgtk-4-1 libwebkitgtk-6.0-4
# Fedora
sudo dnf install gtk4 webkitgtk6.0WebKitGTK 4.0, GTK 3, and libsoup 2 do not satisfy this requirement and must not be substituted with ABI symlinks. The package provides Linux x64 and arm64 sidecars.
npm install @rcompat/webviewpnpm add @rcompat/webviewyarn add @rcompat/webviewbun add @rcompat/webviewUsage#
Basic webview#
import platform from "@rcompat/webview/default";
import Webview from "@rcompat/webview";
const webview = new Webview({ platform });
webview.navigate("https://example.com");
webview.run();Set HTML content directly#
import platform from "@rcompat/webview/default";
import Webview from "@rcompat/webview";
const webview = new Webview({ platform });
webview.html = `
<!DOCTYPE html>
<html>
<body>
<h1>Hello from Webview!</h1>
</body>
</html>
`;
webview.run();Custom window size#
import platform from "@rcompat/webview/default";
import Webview from "@rcompat/webview";
const webview = new Webview({ platform });
webview.size = {
width: 800,
height: 600,
hint: 0, // 0=none, 1=min, 2=max, 3=fixed
};
webview.navigate("https://example.com");
webview.run();Debug mode#
import platform from "@rcompat/webview/default";
import Webview from "@rcompat/webview";
// enable developer tools
const webview = new Webview({ platform, debug: true });
webview.navigate("https://example.com");
webview.run();Server with webview in worker#
Create a local server and open it in a webview using a worker for non-blocking operation:
worker.ts
import platform from "@rcompat/webview/default";
import Webview from "@rcompat/webview";
const webview = new Webview({ platform });
webview.navigate("http://localhost:8181");
webview.run();app.ts
import http from "@rcompat/http";
const headers = { "Content-Type": "text/html" };
const html = `
<html>
<body>
<h1>Desktop App</h1>
<a href="https://github.com">GitHub</a>
</body>
</html>
`;
http.serve(() => new Response(html, { headers }), {
host: "localhost",
port: 8181,
});
// Bun
new Worker("worker.ts");
// Deno
new Worker(new URL("worker.ts", import.meta.url).href, { type: "module" });API Reference#
Webview#
import Webview from "@rcompat/webview";
import platform from "@rcompat/webview/default";
import type {
Init, Size, SizeHint, Webview as WebviewContract,
} from "@rcompat/webview";
const init: Init = { platform };
const webview: WebviewContract = new Webview(init);Constructor Options#
| Property | Type | Default | Description |
|---|---|---|---|
platform |
BunFile | string |
— | Platform binary (required) |
debug |
boolean |
false |
Enable developer tools |
Properties#
| Property | Type | Description |
|---|---|---|
html |
string | undefined |
Read or set the latest HTML content |
size |
Required<Size> |
Read the effective size or set partial dimensions |
Size Object#
| Property | Type | Default | Description |
|---|---|---|---|
width |
number |
1280 |
Window width in pixels |
height |
number |
720 |
Window height in pixels |
hint |
0-3 |
0 |
0=none, 1=min, 2=max, 3=fixed |
All size properties are optional when assigning webview.size; omitted values
use the defaults shown above. Reading webview.size returns a snapshot, so
mutating that object does not resize the native window. Assign a new partial
size object to resize it. SizeHint is the union 0 | 1 | 2 | 3.
Methods#
| Method | Description |
|---|---|
navigate(url) |
Navigate to a URL |
run() |
Start the webview event loop (blocking) |
destroy() |
Idempotently terminate and clean up the webview |
run() destroys the native handle after the event loop exits. When running in
a worker, destruction posts the message "destroyed"; main-thread use does not
require postMessage to exist.
Platform Exports#
Auto-detect the current platform:
import platform from "@rcompat/webview/default";Or replace that import with exactly one explicit target:
import platform from "@rcompat/webview/darwin-arm64"; // macOS ARMimport platform from "@rcompat/webview/darwin-x64"; // macOS Intelimport platform from "@rcompat/webview/linux-arm64"; // Linux ARM64import platform from "@rcompat/webview/linux-x64"; // Linux x64import platform from "@rcompat/webview/windows-x64"; // Windows x64Only the six platform paths shown above are exported. Platform selection is kept explicit so Bun and Deno compilers can include the selected sidecar.
Compiling to Executable#
Bun#
bun build app.ts worker.ts --compile --minifyDeno#
deno compile --no-check --include worker.ts app.tsCross-Compilation#
Generate platform-specific builds for distribution:
build.ts
import fs from "@rcompat/fs";
import runtime from "@rcompat/runtime";
const prefix = "--platform=";
const platform = runtime.args
.find(argument => argument.startsWith(prefix))
?.slice(prefix.length) ?? "default";
await fs.ref(import.meta.dirname).join("worker.ts").write(`
import platform from "@rcompat/webview/${platform}";
import Webview from "@rcompat/webview";
const webview = new Webview({ platform });
webview.navigate("http://localhost:8181");
webview.run();
`);Build for Linux from macOS#
# Bun
bun build.ts --platform=linux-x64
bun build app.ts worker.ts --compile --target=bun-linux-x64 --minify
# Deno
deno -A build.ts --platform=linux-x64
deno compile --no-check --include worker.ts --target=x86_64-unknown-linux-gnu app.tsExamples#
Desktop app with graceful shutdown#
// app.ts
import http from "@rcompat/http";
const html = `
<!DOCTYPE html>
<html>
<body>
<h1>My Desktop App</h1>
<button onclick="alert('Clicked!')">Click Me</button>
</body>
</html>
`;
const server = await http.serve(
() => new Response(html, { headers: { "Content-Type": "text/html" } }),
{ host: "localhost", port: 8181 }
);
// Bun
const worker = new Worker("worker.ts");
// Deno
const worker = new Worker(new URL("worker.ts", import.meta.url).href, {
type: "module",
});
// graceful shutdown when webview closes
worker.addEventListener("message", (event) => {
if (event.data === "destroyed") {
server.stop();
}
});Static HTML app#
import platform from "@rcompat/webview/default";
import Webview from "@rcompat/webview";
const webview = new Webview({ platform, debug: true });
webview.size = { width: 400, height: 300, hint: 3 }; // Fixed size
webview.html = `
<!DOCTYPE html>
<html>
<head>
<style>
body {
font-family: system-ui;
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
margin: 0;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
</style>
</head>
<body>
<h1>Hello, Desktop!</h1>
</body>
</html>
`;
webview.run();Supported Platforms#
| Platform | Architecture | Export |
|---|---|---|
| macOS | ARM64 | darwin-arm64 |
| macOS | x64 | darwin-x64 |
| Linux | ARM64 | linux-arm64 |
| Linux | x64 | linux-x64 |
| Windows | x64 | windows-x64 |
Cross-Runtime Compatibility#
| Runtime | Supported |
|---|---|
| Bun | ✓ |
| Deno | ✓ |
| Node.js | Unsupported (constructor throws) |
License#
MIT
Contributing#
See CONTRIBUTING.md in the repository root.