> ## Documentation Index
> Fetch the complete documentation index at: https://webview.js.org/llms.txt
> Use this file to discover all available pages before exploring further.

# WebviewJS Quickstart: Build Your First Desktop Window

> 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`.

<Steps>
  <Step title="Install WebviewJS">
    Add the package to your project:

    ```bash theme={null}
    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.
  </Step>

  <Step title="Create your first window">
    Create a file called `app.mjs` and add the following:

    ```js app.mjs theme={null}
    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 theme={null}
    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>
  </Step>

  <Step title="Load local HTML">
    Instead of navigating to a URL, pass an `html` string to render content directly without a web server:

    ```js theme={null}
    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.
  </Step>

  <Step title="Handle window events">
    The `Application` instance is a typed `EventEmitter`. Listen for lifecycle events to react to user actions:

    ```js theme={null}
    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.
  </Step>

  <Step title="Add IPC — page to Node">
    The page can send messages to your Node.js code using `window.ipc.postMessage()`. Listen with `webview.onIpcMessage()`:

    ```js theme={null}
    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".
  </Step>
</Steps>

## 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:

```js theme={null}
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`.

<CardGroup cols={2}>
  <Card title="Event Loop" icon="rotate" href="/event-loop">
    Understand how the non-blocking pump works and how to control it.
  </Card>

  <Card title="IPC Messaging" icon="arrows-left-right" href="/guides/ipc-messaging">
    Deep-dive into page ↔ Node communication patterns.
  </Card>

  <Card title="API Reference — Application" icon="cube" href="/api/application">
    Full reference for the Application class, events, and options.
  </Card>

  <Card title="Build Executables" icon="box" href="/guides/building-executables">
    Compile your app into a distributable single-file binary.
  </Card>
</CardGroup>
