> ## 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 IPC: Send Messages Between Page and Node

> Send messages from your web page to Node.js using window.ipc.postMessage, and call Node functions directly from the page using webview.expose().

WebviewJS provides two complementary mechanisms for communication between your web page and Node.js. The first is raw IPC: the page calls `window.ipc.postMessage()` and Node receives the bytes through `webview.onIpcMessage()`. The second is the higher-level `webview.expose()` bridge, which lets you declare a namespace of values and async functions on the page's global scope and call them like ordinary JavaScript. Both mechanisms run on the same thread without an HTTP server.

***

## Sending messages from the page

`wry` automatically injects `window.ipc.postMessage()` into every page. Register your Node-side handler with `webview.onIpcMessage()`, then trigger `postMessage` from any page-side event.

```js theme={null}
import { Application } from '@webviewjs/webview';

const app = new Application();
const win = app.createBrowserWindow({ title: 'IPC Demo' });

const webview = win.createWebview({
  html: '<button id="ping">Ping</button>',
});

webview.onIpcMessage((message) => {
  console.log(message.body.toString('utf8'));
});

webview.evaluateScript(`
  document.querySelector('#ping').addEventListener('click', () => {
    window.ipc.postMessage('hello from the page');
  });
`);

app.run();
```

`message.body` is a `Buffer`. Call `.toString('utf8')` (or another encoding) to read it as a string.

***

## Custom IPC channel name

By default the only global available is `window.ipc`. Pass `ipcName` when creating the webview to add an alias for your own namespace. Both names call the same handler.

```js theme={null}
const webview = win.createWebview({
  url: 'app://localhost/index.html',
  ipcName: 'bindings',
});
```

The page can now call `window.bindings.postMessage(...)` or `window.ipc.postMessage(...)` interchangeably.

<Warning>
  On Windows, `window.ipc.postMessage()` does not fire when the page is loaded
  from a `file:` URL. Load IPC-enabled pages through a custom protocol such as
  `app://` instead. See [Custom Protocols](/guides/custom-protocols).
</Warning>

***

## Sending data from Node to page

Use `evaluateScript()` for one-way updates — DOM manipulation, setting a title, or injecting data.

```js theme={null}
webview.evaluateScript(`
  document.title = 'Connected';
`);
```

Use `evaluateScriptWithCallback()` when you need the evaluated result back in Node. The callback receives a serialized string result (or an error).

```js theme={null}
webview.evaluateScriptWithCallback('document.title', (error, title) => {
  if (error) throw error;
  console.log('Page title:', title);
});
```

***

## JSON messages

IPC message bodies are raw bytes. Using JSON is a practical convention for structured data without building a custom binary protocol.

```js theme={null}
// In the page
window.ipc.postMessage(JSON.stringify({ action: 'save', payload: { id: 1 } }));

// In Node.js
webview.onIpcMessage((message) => {
  const data = JSON.parse(message.body.toString('utf8'));
  console.log(data.action); // "save"
  console.log(data.payload.id); // 1
});
```

<Tip>
  If you find yourself building a request/response protocol on top of raw IPC,
  consider using `webview.expose()` instead — it handles serialization,
  routing, and Promise resolution for you.
</Tip>

***

## The expose() bridge

`webview.expose(name, target)` is the higher-level alternative to raw IPC. It registers a namespace on `window` in the page. Static JSON-serializable values appear directly on the namespace; async functions are wrapped so that every call from the page returns a `Promise`.

**Node side** — expose a namespace with static values and async functions:

```js theme={null}
import { readFile } from 'node:fs/promises';
import { Application } from '@webviewjs/webview';

const app = new Application();
const win = app.createBrowserWindow({ title: 'Expose Demo' });

// Register the protocol before creating the webview
win.registerProtocol('app', async (request) => {
  // ... serve your files
});

const webview = win.createWebview({ url: 'app://localhost/index.html' });

webview.expose('native', {
  isCool: true,
  version: '1.0.0',
  readFile: async (path) => readFile(path, 'utf8'),
});

app.run();
```

**Page side** — access the namespace immediately for static values and `await` functions:

```js theme={null}
console.log(window.native.isCool);   // true
console.log(window.native.version);  // "1.0.0"

const text = await window.native.readFile('/tmp/example.txt');
console.log(text);
```

***

## expose() rules and limits

**What you can expose:**

* JSON-serializable scalar values: strings, numbers, booleans, `null`, plain objects, arrays
* `async` functions (or functions that return a `Promise`)

**What you cannot expose:**

| Unsupported                     | Reason                                              |
| ------------------------------- | --------------------------------------------------- |
| Getters and setters             | Only own enumerable data properties are read        |
| `BigInt` values or arguments    | Not JSON-serializable                               |
| Cyclic object structures        | Not JSON-serializable                               |
| Functions as argument values    | Arguments are serialized before crossing the bridge |
| Functions returning `undefined` | `undefined` is not JSON-serializable                |

**Namespace uniqueness:** Each namespace name can only be exposed once per `Webview` instance. Calling `expose()` with a name that is already registered throws immediately.

<Note>
  When serialization fails — for example because you return `undefined` from an
  exposed function or pass a `BigInt` as an argument — WebviewJS throws a
  `SerializationError`. Catch it in your async function or handle it in a
  `.catch()` on the Promise returned by the page.
</Note>

***

## Related pages

* [Custom Protocols](/guides/custom-protocols) — serve local files over `app://` (required for IPC on Windows)
* [Webview API reference](/api/webview) — full method signatures for `onIpcMessage`, `evaluateScript`, `evaluateScriptWithCallback`, and `expose`
