# Understanding Omarchy's Menu Guards and Providers: A Deep Dive into Dynamic Desktop Menus

> Explore Omarchy's menu guards and providers. Learn how guards conditionally control menu entries and providers dynamically populate submenus at runtime for advanced desktop menu customization.

- Repository: [37signals/omarchy](https://github.com/basecamp/omarchy)
- Tags: deep-dive
- Published: 2026-08-20

---

**Omarchy's menu system uses guards to conditionally show, mark, or disable entries based on Bash expressions, and providers to dynamically populate submenus at runtime.**

The Quickshell desktop **plugin `omarchy.menu`** powers Omarchy's entire menu infrastructure. According to the `basecamp/omarchy` source code in [`shell/plugins/menu/MenuModel.js`](https://github.com/basecamp/omarchy/blob/main/shell/plugins/menu/MenuModel.js), the system separates static menu definitions from dynamic runtime behavior through two core mechanisms: **guards** for conditional visibility and state, and **providers** for on-demand content generation.

## What Are Menu Guards?

Guards are **Bash-style expressions** attached to menu entries via three keys: `when`, `checked`, and `disabled`. These expressions are documented in [`docs/menu.md`](https://github.com/basecamp/omarchy/blob/main/docs/menu.md) and evaluated in a **single batched Bash process** per menu reload, minimizing overhead.

### The Three Guard Types

Each guard serves a distinct purpose in controlling menu presentation:

- **`when`** — Hides the entire row if the expression fails (returns false). For submenus, if all visible descendants are hidden, the submenu itself disappears—except provider-backed submenus, which remain and load on demand.

- **`checked`** — Adds a "✓" marker when the expression succeeds, indicating the current active choice. Used for defaults, DNS configurations, channel selections, and similar stateful options.

- **`disabled`** — Keeps the row visible but dims it and makes it unselectable. Disabled rows also receive the "✓" marker to signal that the item is already installed or active.

The batching system outputs results in a structured format: `<id>:w:<0|1>` for `when`, `<id>:c:<0|1>` for `checked`, and `<id>:d:<0|1>` for `disabled`.

### Guard Helper Functions

Performance-critical helper functions like `omarchy-pkg-present` and `omarchy-cmd-present` are defined in **[`MenuModel.js`](https://github.com/basecamp/omarchy/blob/main/MenuModel.js)** at line 104 and injected into the guard script via `guardHelpers()`. This design eliminates the overhead of spawning multiple Bash processes for each expression.

```jsonc
// Example: Guard usage to conditionally show/hide based on git installation
{
  "install.git": {
    "label": "Git",
    "action": "omarchy-pkg-add git",
    "when": "omarchy-pkg-missing git",
    "disabled": "omarchy-pkg-present git"
  }
}

```

In this example, the `when` guard keeps the row visible only if git is not installed, while `disabled` dims it with a checkmark when git is already present.

## What Are Menu Providers?

Providers supply submenu rows **at runtime** rather than from static JSONC definitions. An entry with `provider: "<name>"` delegates row generation to a named provider implementation.

### Built-in Provider Types

The `providers` map in **`Menu.qml`** defines several provider types:

- **`apps`** — Native QML provider that enumerates desktop applications with icons, launch feedback, and uninstall support.

- **`fonts`** and **`power-profiles`** — Bash one-liners returning tab-delimited lines in the format `label\tvalue\tcurrent`.

```jsonc
// Example: Provider usage for dynamic font listing
{
  "fonts": {
    "label": "Fonts",
    "provider": "fonts",
    "icon": "f"
  }
}

```

When the user opens the "Fonts" submenu, the `fonts` provider executes a Bash script emitting lines like `Monospace\tmonospace\t$(omarchy-default-font)`, and the menu constructs rows dynamically.

### Volatile Providers

Providers marked with the **`volatile`** flag re-run every time their submenu is entered. This ensures newly installed fonts or changed system states appear immediately without restarting the shell.

## Implementation Architecture

The core logic resides in **[`shell/plugins/menu/MenuModel.js`](https://github.com/basecamp/omarchy/blob/main/shell/plugins/menu/MenuModel.js)**:

- `swapProviderRows()` at line 36 merges provider-returned rows while preserving existing static items.
- Guard batching and evaluation orchestrate the conditional logic.
- JSONC parsing and item merging handle the static configuration from `default/omarchy/omarchy-menu.jsonc`.

Adding a new provider requires only two steps: extend the `providers` map in `Menu.qml`, then reference it via `provider:` in any JSONC entry.

## Key Files Reference

| File | Purpose |
|------|---------|
| [`docs/menu.md`](https://github.com/basecamp/omarchy/blob/main/docs/menu.md) | Schema documentation for guards and providers |
| [`shell/plugins/menu/MenuModel.js`](https://github.com/basecamp/omarchy/blob/main/shell/plugins/menu/MenuModel.js) | Core JavaScript implementation |
| `default/omarchy/omarchy-menu.jsonc` | Default static menu definition |

## Summary

- **Guards** (`when`, `checked`, `disabled`) control visibility and state using batched Bash expressions with helper functions from [`MenuModel.js`](https://github.com/basecamp/omarchy/blob/main/MenuModel.js).

- **Providers** generate submenu content dynamically; native QML providers handle complex cases while Bash one-liners handle simple lists.

- The **volatile** flag enables real-time updates without shell restarts.

- All guard expressions execute in a **single Bash process** for performance.

- Custom providers integrate by extending `Menu.qml` and referencing them in JSONC entries.

## Frequently Asked Questions

### How do Omarchy menu guards improve performance?

Omarchy's menu guards execute all expressions in a single batched Bash process per menu reload, returning structured output like `<id>:w:<0|1>`. This design eliminates the overhead of spawning separate processes for each guard check, which would be prohibitively expensive in a frequently-accessed desktop menu.

### Can providers and static entries coexist in the same submenu?

Yes. The `swapProviderRows()` function in [`MenuModel.js`](https://github.com/basecamp/omarchy/blob/main/MenuModel.js) merges dynamically generated provider rows with existing static items. This allows hybrid submenus that combine fixed entries with runtime-generated content.

### What happens if all entries in a submenu are hidden by `when` guards?

The submenu itself disappears—unless it is provider-backed. Provider-backed submenus remain visible and load on demand when entered, ensuring dynamic content is always accessible even when static entries are conditionally hidden.

### How do I add a custom provider to Omarchy's menu?

Extend the `providers` map in `shell/plugins/menu/Menu.qml` with your provider name and implementation (either a Bash command or native QML handler). Then reference it in any JSONC menu entry using `"provider": "your-name"`. Volatile providers can be marked to refresh on every submenu entry.