# Node <-> Renderer

Positron uses a decoupled, event-driven architecture where the Node.js main process and the native web view (Renderer process) communicate via an asynchronous Inter-Process Communication (IPC) bridge. This bridge is abstractly wrapped around a localized WebSocket channel, making it easy to pass messages back and forth.

### Node.js ➔ Renderer

To push an arbitrary data payload down into a specific window context from your Node.js backend, use the `emitToRenderer` method exposed on the `Window` instance.

#### &#x20;`window.emitToRenderer(command, args)`

* `command` `(String)`: The channel or event identifier the frontend is listening for.
* `args` `(Array|Object)`: Contextual payload or arguments passed directly into the frame execution flow.

#### Implementation Example:

<pre class="language-javascript" data-line-numbers><code class="lang-javascript">mainWindow.on('ready', async () => {
<strong>    mainWindow.emitToRenderer('hello-world', { 
</strong><strong>        message: 'Hello from the main process!' 
</strong><strong>    });
</strong>});
</code></pre>

<details>

<summary>How sendIpc Works</summary>

When executed, `sendIpc` evaluates the framework state machine:

1. **Ready Check:** Verifies if the local runtime IPC channel is up and actively associated with a valid open connection socket.
2. **Serialization:** Wraps your packet structure tightly with your unique `windowId` context to ensure the native shell targets the correct browser viewport layout.
3. **Execution Safety:** If called before a connection has been safely initialized by the native window runner, it prevents silent failure by outputting a warning to the logging pipeline.

</details>

#### Handle IPC on the Client

The frontend can interact with the native layer via the global `window.ipc` object.

#### `ipc.on(channel, listener)`

Registers a listener function to be executed when the backend emits an event on the specified channel.

* `channel` `(string)`: The channel to subscribe to.
* `listener` `(Function)`: A callback function that receives the `payload` sent from the backend.

```javascript
function handleThemeChange(data) {
    console.log(`Switching theme to: ${data.theme}`);
}

// Subscribe to theme updates
window.ipc.on('theme-changed', handleThemeChange);
```

#### `ipc.off(channel, listener)`

Removes a previously registered listener from a channel.

* `channel` `(string)`: The channel the listener was subscribed to.
* `listener` `(Function)`: The exact reference of the function to remove.

```javascript
// Unsubscribe from theme updates
window.ipc.off('theme-changed', handleThemeChange);
```

#### `ipc.windowId`

* **Type:** `number` (Read-only)

Returns the unique ID of the native window hosting the current webview instance. This is highly useful for frontend routing or targeting specific windows from the backend.

```javascript
console.log(`Current Window ID context: ${window.ipc.windowId}`);
```

### Renderer ➔ Node.js

To call your Node.js backend modules directly from UI user actions (like button clicks, form submissions, or fetch operations), also use the global `window.ipc` object available in your frontend runtime.

#### HTML & Frontend JavaScript Implementation:

<pre class="language-html" data-line-numbers><code class="lang-html">&#x3C;button id="msg-button">Send Greeting to Backend&#x3C;/button>

&#x3C;script>
document.getElementById("msg-button").addEventListener("click", function() {
    // Send an asynchronous frame upstream to Node.js
<strong>    window.ipc.send("hello-world", {
</strong><strong>        message: "Hello from the renderer process!"
</strong><strong>    });
</strong>});
&#x3C;/script>
</code></pre>

### Processing and Responding to Messages on the Backend

Incoming frontend events traverse the IPC Router (`ipc.js`). You handle them in Node.js by mapping listeners onto specific channels via `ipc.handle`.

The router automatically equips your callback handler with context-aware utilities (`reply`, `emit`, and `windowId`) to let you seamlessly pass messages right back down to the source viewport.

{% code lineNumbers="true" %}

```javascript
const { ipc } = require('positron-core');

// Register an active listener on the 'hello-world' channel
ipc.handle('hello-world', (parsedPayload, { reply, emit, windowId }) => {
    console.log(`Received payload from window cluster [ID: ${windowId}]:`, parsedPayload.message);

    // Context Utility 1: Reply directly back to the channel that initiated this request
    reply({ status: "success", receivedAt: Date.now() });

    // Context Utility 2: Broadcast a completely separate event down to the window framework
    emit('status-update', { ongoingJobs: 0 });
});
```

{% endcode %}

<details>

<summary>The Full Life of an IPC Payload</summary>

* **Frontend Dispatch:** `window.ipc.send()` packages your event payload into an `ipcMessage` event frame structure.
* **WebSocket Routing:** The frame passes securely through local TCP bindings on port `9000` directly into the framework core listener engine.
* **Targeted Lookup:** The `IpcRouter` processes the payload string, unrolls the object parameters, and targets your registered functional handler.
* **Execution:** Your business logic operates with safe, structural references to exactly where the message came from and how to answer it.

</details>

### Considerations & Security

* **Private Methods**
  * &#x20;The `window.ipc._emit` function is intended for internal use by the backend only. Frontend applications should not call this method directly.
* **Serialization Constraints**
  * &#x20;All payloads passing back and forth must be completely JSON-serializable. Functions, Symbols, or complex native class instances cannot be transferred.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://positronjs.gitbook.io/v1/ipc/renderer.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
