Skip to content

Latest commit

 

History

History
186 lines (149 loc) · 6.19 KB

File metadata and controls

186 lines (149 loc) · 6.19 KB
title WebviewJS Quickstart: Build Your First Desktop Window
sidebarTitle Quickstart
description Create your first native desktop window with WebviewJS. Load a URL or inline HTML, handle window events, and use IPC in under 30 lines.

In this guide you'll install WebviewJS, open a native OS window, load content into it, react to window events, and send a message from the page back to Node.js. By the end you'll have a working desktop app and an understanding of the core building blocks — Application, BrowserWindow, and Webview.

Add the package to your project:
```bash
npm install @webviewjs/webview
```

Make sure you have Node.js 24 or later and that your platform dependencies are satisfied. See the [Installation guide](/installation) for per-platform setup instructions.
Create a file called `app.mjs` and add the following:
```js app.mjs
import { Application } from '@webviewjs/webview';

const app = new Application();

// Hold strong references so the GC doesn't collect them
let mainWindow = null;
let mainWebview = null;

app.whenReady().then(() => {
  mainWindow = app.createBrowserWindow({
    title: 'My First WebviewJS App',
    width: 1024,
    height: 768,
  });

  mainWebview = mainWindow.createWebview({
    url: 'https://example.com',
  });
});
```

Run it:

```bash
node app.mjs
```

A native window opens and loads `https://example.com`. Close the window to exit.

<Note>
  Always store `BrowserWindow` and `Webview` instances in variables that live as long as you need them. If you let these references be garbage-collected, the native handles become unreachable and their event listeners stop firing.
</Note>
Instead of navigating to a URL, pass an `html` string to render content directly without a web server:
```js
mainWebview = mainWindow.createWebview({
  html: `<!DOCTYPE html>
<html lang="en">
  <head><meta charset="UTF-8"><title>Hello</title></head>
  <body>
    <h1>Hello from WebviewJS!</h1>
    <p>Running on Node.js ${process.version}</p>
  </body>
</html>`,
});
```

Use the `html` option for self-contained pages, prototypes, or any scenario where you want to serve content without a protocol handler or HTTP server.
The `Application` instance is a typed `EventEmitter`. Listen for lifecycle events to react to user actions:
```js
app.whenReady().then(() => {
  mainWindow = app.createBrowserWindow({ title: 'My App', width: 1024, height: 768 });
  mainWebview = mainWindow.createWebview({ url: 'https://example.com' });

  // Fires when all windows have been closed
  app.on('application-close-requested', () => {
    console.log('All windows closed — exiting.');
    app.exit();
  });

  // Fires when a single window's close button is clicked
  app.on('window-close-requested', () => {
    console.log('A window close was requested.');
  });
});
```

Without the `application-close-requested` handler calling `app.exit()`, the process stays alive after the last window closes because the event pump timer keeps Node running.
The page can send messages to your Node.js code using `window.ipc.postMessage()`. Listen with `webview.onIpcMessage()`:
```js
app.whenReady().then(() => {
  mainWindow = app.createBrowserWindow({ title: 'IPC Demo', width: 800, height: 600 });

  mainWebview = mainWindow.createWebview({
    html: `<!DOCTYPE html>
<html>
  <body>
    <h1 id="status">Waiting…</h1>
    <button onclick="window.ipc.postMessage('ping')">Send ping</button>
  </body>
</html>`,
  });

  mainWebview.onIpcMessage((msg) => {
    const text = msg.body.toString('utf-8');
    console.log('Received from page:', text); // "ping"

    // Send a reply back into the page
    mainWebview.evaluateScript(
      `document.getElementById('status').textContent = 'Node replied: pong'`
    );
  });
});
```

Click the button in the window — "ping" appears in your terminal and the page updates to show "Node replied: pong".

Going further: expose async functions

For a cleaner request/response pattern, use webview.expose() to publish an entire namespace of Node.js functions directly to the page. Every exposed function automatically becomes a Promise in the browser context:

import { Application } from '@webviewjs/webview';
import { readFile } from 'node:fs/promises';

const app = new Application();
let mainWindow = null;
let mainWebview = null;

app.whenReady().then(() => {
  mainWindow = app.createBrowserWindow({ title: 'Expose Demo', width: 800, height: 600 });

  mainWebview = mainWindow.createWebview({
    html: `<!DOCTYPE html>
<html>
  <body>
    <pre id="out">Loading config…</pre>
    <script>
      window.native.readConfig().then(cfg => {
        document.getElementById('out').textContent = JSON.stringify(cfg, null, 2);
      });
    </script>
  </body>
</html>`,
  });

  // Expose a namespace called "native" to the page
  mainWebview.expose('native', {
    version: '1.0.0',
    readConfig: async () => JSON.parse(await readFile('./config.json', 'utf-8')),
  });
});

Values, arguments, and return types must be JSON-serializable. Violations throw a SerializationError.

Understand how the non-blocking pump works and how to control it. Deep-dive into page ↔ Node communication patterns. Full reference for the Application class, events, and options. Compile your app into a distributable single-file binary.