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

# TrayIcon API — System Tray Icons, Menus, and Events

> Create and manage a native system tray icon with menus, tooltips, and click events. Supports runtime icon updates and per-platform configuration.

A system tray icon lets your application live persistently in the operating system's notification area (system tray) even when no visible window is open. You create tray icons through `app.createTrayIcon()` — this ensures creation happens on the event-loop thread, which is required for native UI objects on all three platforms.

***

## Creating a tray icon

Call `app.createTrayIcon(TrayIconOptions)` inside (or after) `app.whenReady()` to guarantee the event loop is running before the native tray icon is registered.

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

const app = new Application();

// Build a 16×16 solid-blue RGBA icon in memory
const size = 16;
const rgba = Buffer.alloc(size * size * 4);
for (let i = 0; i < rgba.length; i += 4) {
  rgba[i]     = 70;   // R
  rgba[i + 1] = 150;  // G
  rgba[i + 2] = 240;  // B
  rgba[i + 3] = 255;  // A (fully opaque)
}

let tray;

app.whenReady().then(() => {
  tray = app.createTrayIcon({
    id: 'main',
    icon: { data: rgba, width: size, height: size },
    tooltip: 'My Application',
    menu: {
      items: [
        { id: 'show', label: 'Show window' },
        { role: 'separator' },
        { id: 'quit', label: 'Quit' },
      ],
    },
  });

  tray.on('click', ({ button, buttonState }) => {
    console.log('Tray clicked:', button, buttonState);
  });
});

// Handle tray menu selections
app.on('custom-menu-click', ({ customMenuEvent }) => {
  if (customMenuEvent.id === 'show') win.show();
  if (customMenuEvent.id === 'quit') app.exit();
});

const win = app.createBrowserWindow({ title: 'Tray Example' });
win.createWebview({ html: '<h1>Running in the tray</h1>' });

app.on('application-close-requested', () => app.exit());
app.run();
```

***

## TrayIconOptions

Pass a `TrayIconOptions` object to `app.createTrayIcon()`.

```ts theme={null}
interface TrayIconOptions {
  id?:               string;
  icon?:             TrayIconImage;
  tooltip?:          string;
  title?:            string;
  menu?:             MenuOptions;
  iconIsTemplate?:   boolean;
  menuOnLeftClick?:  boolean;
  menuOnRightClick?: boolean;
}

interface TrayIconImage {
  data:    Buffer;
  width?:  number;
  height?: number;
}
```

| Field              | Type            | Description                                                                                                  |
| ------------------ | --------------- | ------------------------------------------------------------------------------------------------------------ |
| `id`               | `string`        | Optional unique identifier for this tray icon. Auto-generated if omitted.                                    |
| `icon`             | `TrayIconImage` | The icon to display. See below for raw RGBA vs encoded image formats.                                        |
| `tooltip`          | `string`        | Text shown when the user hovers over the tray icon. Not supported on Linux.                                  |
| `title`            | `string`        | Text displayed next to the icon in the menu bar. **macOS only.**                                             |
| `menu`             | `MenuOptions`   | Context menu shown when the user right-clicks the icon (or left-clicks, if `menuOnLeftClick` is set).        |
| `iconIsTemplate`   | `boolean`       | Treat the icon as a monochrome template that adapts to dark/light mode. **macOS only.**                      |
| `menuOnLeftClick`  | `boolean`       | Show the context menu on a left-click. Not supported on Linux.                                               |
| `menuOnRightClick` | `boolean`       | Show the context menu on a right-click (default platform behaviour on most systems). Not supported on Linux. |

### Icon data formats

The `icon.data` field accepts two formats:

* **Raw RGBA bytes** — supply `width` and `height` alongside a `Buffer` whose length equals `width × height × 4`. Each pixel is four bytes: R, G, B, A.
* **Encoded image bytes** — supply a `Buffer` containing a PNG, JPEG, WebP, GIF, BMP, ICO, or TIFF file without `width` or `height`. The native backend decodes the image automatically.

***

## Methods

Once you have a `TrayIcon` instance returned by `app.createTrayIcon()`, you can call these methods at any time before the icon is disposed.

### `tray.id`

```ts theme={null}
tray.id: string
```

Read-only string identifier set at creation time (or auto-generated).

***

### `tray.setIcon(data, width?, height?)`

```ts theme={null}
tray.setIcon(data: Buffer, width?: number, height?: number): void
```

Replace the tray icon image at runtime. Supply raw RGBA `data` with `width` and `height`, or encoded image bytes without dimensions.

***

### `tray.removeIcon()`

```ts theme={null}
tray.removeIcon(): void
```

Remove and hide the icon from the tray area. The `TrayIcon` instance remains valid and you can restore it by calling `setIcon()`.

***

### `tray.setMenu(menu?)`

```ts theme={null}
tray.setMenu(menu?: MenuOptions): void
```

Replace the context menu. Pass `undefined` or call without arguments to remove the menu entirely.

***

### `tray.setTooltip(tooltip?)`

```ts theme={null}
tray.setTooltip(tooltip?: string): void
```

Update the hover tooltip text. Not supported on Linux.

***

### `tray.setTitle(title?)`

```ts theme={null}
tray.setTitle(title?: string): void
```

Set or clear the text label shown next to the icon. **macOS only.** On other platforms this call is a no-op.

***

### `tray.setVisible(visible)`

```ts theme={null}
tray.setVisible(visible: boolean): void
```

Show or hide the tray icon without removing it. Hidden icons retain their configuration and can be made visible again.

***

### `tray.setIconAsTemplate(value)`

```ts theme={null}
tray.setIconAsTemplate(value: boolean): void
```

Mark the icon as a monochrome template image. When `true`, macOS renders the icon using the appropriate foreground colour for the current menu-bar appearance (dark or light). **macOS only.**

***

### `tray.setShowMenuOnLeftClick(value)`

```ts theme={null}
tray.setShowMenuOnLeftClick(value: boolean): void
```

Control whether a left-click opens the context menu. Not supported on Linux.

***

### `tray.setShowMenuOnRightClick(value)`

```ts theme={null}
tray.setShowMenuOnRightClick(value: boolean): void
```

Control whether a right-click opens the context menu. Not supported on Linux.

***

### `tray.showMenu()`

```ts theme={null}
tray.showMenu(): void
```

Programmatically open the tray context menu from your Node.js code, without waiting for a user click.

***

### `tray.rect()`

```ts theme={null}
tray.rect(): TrayRect | null
```

Return the bounding rectangle of the tray icon on screen, or `null` if the platform does not expose it.

```ts theme={null}
interface TrayRect {
  x:      number;
  y:      number;
  width:  number;
  height: number;
}
```

***

## Tray events

`TrayIcon` is a Node.js `EventEmitter`. Register listeners with `tray.on(event, handler)`.

| Event          | Payload fields                                    | Description                                   |
| -------------- | ------------------------------------------------- | --------------------------------------------- |
| `click`        | `id`, `x`, `y`, `rect`, `button?`, `buttonState?` | Fired when the user clicks the icon.          |
| `double-click` | `id`, `x`, `y`, `rect`, `button?`, `buttonState?` | Fired on a double-click.                      |
| `enter`        | `id`, `x`, `y`, `rect`                            | Cursor entered the icon's bounding rectangle. |
| `move`         | `id`, `x`, `y`, `rect`                            | Cursor is moving over the icon.               |
| `leave`        | `id`, `x`, `y`, `rect`                            | Cursor left the icon's bounding rectangle.    |

```ts theme={null}
interface TrayEventPayload {
  event:        string;
  id:           string;
  x:            number;
  y:            number;
  rect:         TrayRect;
  button?:      string;
  buttonState?: string;
}
```

```js theme={null}
tray.on('click', ({ button, buttonState, x, y }) => {
  console.log(`Tray icon clicked at (${x}, ${y}) — button: ${button}, state: ${buttonState}`);
});

tray.on('enter', () => console.log('Cursor entered tray icon'));
tray.on('leave', () => console.log('Cursor left tray icon'));
```

***

## Menu click events

Clicking a menu item in the tray's context menu does **not** fire a tray event. Instead, it fires the `custom-menu-click` event on the `app` instance, exactly the same as window menu clicks. Use `customMenuEvent.id` to identify which item was selected.

```js theme={null}
app.on('custom-menu-click', ({ customMenuEvent }) => {
  switch (customMenuEvent.id) {
    case 'show': win.show();   break;
    case 'quit': app.exit();  break;
  }
});
```

See the [Menu — Handling menu clicks](/api/menu#handling-menu-clicks) section for full details.

***

## Platform notes

| Feature                                 | Windows         | macOS       | Linux           |
| --------------------------------------- | --------------- | ----------- | --------------- |
| `title` (text next to icon)             | ✗ Not supported | ✓ Supported | ✗ Not supported |
| `tooltip`                               | ✓ Supported     | ✓ Supported | ✗ Not supported |
| `menuOnLeftClick` / `menuOnRightClick`  | ✓ Supported     | ✓ Supported | ✗ Not supported |
| Template icons (`setIconAsTemplate`)    | ✗ Not supported | ✓ Supported | ✗ Not supported |
| Pointer events (`click`, `enter`, etc.) | ✓ Emitted       | ✓ Emitted   | ✗ Not emitted   |

***

## Disposal

Call `tray.dispose()` to remove the icon from the system tray immediately. You can also use the ECMAScript explicit resource management syntax with `Symbol.dispose`.

```js theme={null}
// Explicit disposal
tray.dispose();

// Or using `using` (ES2025 / TypeScript 5.2+)
{
  using tray = app.createTrayIcon({ /* … */ });
  // tray is automatically disposed when the block exits
}
```

Check whether an icon has already been disposed:

```js theme={null}
if (!tray.isDisposed()) {
  tray.setTooltip('Still alive');
}
```

When `app.exit()` is called, the `Application` object removes all tray icons it owns, regardless of whether the `TrayIcon` wrapper object is still reachable in your code. You do not need to call `tray.dispose()` before `app.exit()`.
