# How to Implement Context Menus, Dialogs, and System Notifications in the Native SDK

> Learn to implement context menus, dialogs, and system notifications in the Native SDK. Import runtime widgets and initialize Zig structs with ease.

- Repository: [Vercel Labs/native](https://github.com/vercel-labs/native)
- Tags: how-to-guide
- Published: 2026-07-18

---

**You can implement context menus, dialogs, and system notifications in the Native SDK by importing the runtime widgets in `src/runtime/canvas_widget_context_menu.zig`, `src/runtime/canvas_widget_dialog.zig`, and `src/runtime/system_notification.zig`, then initializing their Zig structs with items, callbacks, and content panes.**

The `vercel-labs/native` repository is a cross-platform UI framework written in Zig that lets you build native desktop applications using canvas primitives and declarative widgets. If you want to implement context menus, dialogs, and system notifications in the Native SDK, you will work primarily with the runtime widget layer located in `src/runtime/`. This article walks through the exact file paths, APIs, and code patterns used to add interactive menus, modal overlays, and OS-level notifications to your app.

## Native SDK UI Architecture

### Canvas Primitives Layer

The lowest layer exposes a platform-agnostic drawing API for paths, text, and icons. These **canvas primitives** live in `src/primitives/canvas` and provide the rendering foundation that all higher-level widgets consume.

### Runtime Widget Layer

Concrete interactive components that combine canvas drawing with mouse and keyboard handling live in `src/runtime/`. This layer manages state, boundary clamping, and event emission for components such as context menus and dialogs.

### App Manifest Layer

Applications declare how widgets fit together through a small declarative manifest. The `app.zon` files inside `examples/*/app.zon` wire widgets to the event loop and define the main application entry point.

## Implementing Context Menus in the Native SDK

Context menus are implemented in `src/runtime/canvas_widget_context_menu.zig`. According to the `vercel-labs/native` source code, the widget performs four core tasks:

1. **Registers** a right-click or long-press handler on any canvas element.
2. **Builds** a vertical list of selectable items using canvas text and icon primitives.
3. **Positions** itself automatically based on the click point, clamping to window bounds.
4. **Emits** a `MenuSelect` event that the application listens to and acts upon.

The public API is a small struct:

```zig
pub const ContextMenu = struct {
    items: []const MenuItem,
    onSelect: fn (selected: usize) void,
    // ...internal state
};

```

A `MenuItem` holds a label and an optional icon sourced from `src/primitives/canvas/icons/menu.svg`. The `onSelect` callback receives the index of the chosen entry.

Here is a minimal example that attaches a context menu to the root canvas:

```zig
// src/main.zig
const std = @import("std");
const native = @import("native");
const ContextMenu = @import("runtime/canvas_widget_context_menu.zig").ContextMenu;

pub fn main() void {
    var app = native.App.init(.{
        .title = "Context-Menu Demo",
        .width = 400,
        .height = 300,
    });

    const items = [_]ContextMenu.MenuItem{
        .{ .label = "Copy", .icon = null },
        .{ .label = "Paste", .icon = null },
        .{ .label = "Delete", .icon = null },
    };

    app.canvas.setContextMenu(ContextMenu{
        .items = &items,
        .onSelect = onMenuSelect,
    });

    app.run();
}

fn onMenuSelect(selected: usize) void {
    const labels = [_][]const u8{ "Copy", "Paste", "Delete" };
    std.debug.print("User selected: {s}\n", .{labels[selected]});
}

```

When the user right-clicks the canvas, the menu appears and selecting an item prints the choice.

## Creating Modal Dialogs in the Native SDK

Modal dialogs are built on top of the same canvas widget system. The dialog component in `src/runtime/canvas_widget_dialog.zig` composes three visual elements:

- A **background overlay** that captures clicks outside the dialog to dismiss it.
- A **content pane** that hosts arbitrary canvas widgets such as text, inputs, and buttons.
- **Action buttons** (for example, "OK" and "Cancel") that emit a `DialogResult` event.

Typical usage involves creating a `Dialog` instance, populating its `content` list, and supplying callbacks for each button. The dialog automatically centers itself and disables interaction with underlying widgets while open.

The following example shows a save confirmation dialog:

```zig
const Dialog = @import("runtime/canvas_widget_dialog.zig").Dialog;

fn showSaveDialog(app: *native.App) void {
    var dlg = Dialog{
        .title = "Save File",
        .content = &[_]Dialog.Content{
            .{ .type = .Text, .value = "Enter filename:" },
            .{ .type = .Input, .value = "" },
        },
        .buttons = &[_]Dialog.Button{
            .{ .label = "Cancel", .role = .Cancel, .onPress = onCancel },
            .{ .label = "Save",   .role = .Primary, .onPress = onSave },
        },
    };
    app.showDialog(&dlg);
}

fn onCancel() void {
    std.debug.print("Dialog cancelled\n", .{});
}

fn onSave() void {
    std.debug.print("Proceed with save operation\n", .{});
}

```

The dialog blocks interaction with the rest of the UI until a button is pressed.

## Sending System Notifications in the Native SDK

Native-level system notifications are abstracted in `src/runtime/system_notification.zig`. As implemented in `vercel-labs/native`, the SDK translates a high-level `Notification` struct into platform-specific APIs:

- **macOS** – `NSUserNotification` via the Objective-C bridge.
- **Windows** – Win32 toast notification APIs.
- **Linux** – D-Bus `org.freedesktop.Notifications` messages.

The API is straightforward:

```zig
pub const Notification = struct {
    title: []const u8,
    body: []const u8,
    iconPath: ?[]const u8,
    // optional callback for click action
    onClick: ?fn () void,
};

pub fn push(notif: Notification) void;

```

Calling `push` schedules the notification with the host OS. The SDK handles permission requests and fallback behavior on platforms that lack native support.

Here is an example that pushes a welcome notification:

```zig
const Notification = @import("runtime/system_notification.zig").Notification;

fn pushWelcome(app: *native.App) void {
    const note = Notification{
        .title = "Welcome!",
        .body = "Your Native app is running.",
        .iconPath = null,
        .onClick = onNotificationClick,
    };
    Notification.push(note);
}

fn onNotificationClick() void {
    std.debug.print("User clicked the notification\n", .{});
}

```

When triggered, this displays a native OS notification, and tapping it runs `onNotificationClick`.

## Summary

- **`src/runtime/canvas_widget_context_menu.zig`** provides the **context menu** widget that binds to right-click events and returns a selected index through a callback.
- **`src/runtime/canvas_widget_dialog.zig`** implements **modal dialogs** with an overlay, content pane, and configurable action buttons that emit result events.
- **`src/runtime/system_notification.zig`** exposes a cross-platform **notification** API that maps to macOS, Windows, and Linux native backends.
- All three features rely on the **canvas primitives** in `src/primitives/canvas` and are wired together through the app manifest pattern found in `examples/*/app.zon`.

## Frequently Asked Questions

### How do I attach a context menu to a specific canvas element?

You initialize a `ContextMenu` struct with an array of `MenuItem` entries and an `onSelect` callback, then attach it via `app.canvas.setContextMenu()`. The widget in `src/runtime/canvas_widget_context_menu.zig` registers the right-click handler and positions the menu automatically.

### Can I customize the buttons and layout inside a Native SDK dialog?

Yes. The `Dialog` struct defined in `src/runtime/canvas_widget_dialog.zig` exposes a `content` slice that accepts arbitrary canvas widgets including text and input fields. You define buttons in the `buttons` slice and assign each a `role` and `onPress` callback.

### Which platforms support system notifications out of the box?

The Native SDK supports system notifications on **macOS**, **Windows**, and **Linux**. The `src/runtime/system_notification.zig` module translates the generic `Notification` struct into `NSUserNotification`, Win32 toasts, or D-Bus messages depending on the host OS.

### Where are the canvas primitives for drawing UI elements defined?

Low-level drawing primitives live in `src/primitives/canvas`. These provide the platform-agnostic paths, text rendering, and icon utilities that both `canvas_widget_context_menu.zig` and `canvas_widget_dialog.zig` consume to render their interfaces.