How the Omarchy Menu System Works: Entries, Guards, and Providers Explained

Omarchy’s menu system uses JSONC-defined entries with Bash-based guards for conditional visibility and state, alongside dynamic providers that supply submenus via IPC, all batched through a subprocess to keep the UI responsive.

The Omarchy menu system powers the desktop environment's dynamic, context-aware application launcher through a declarative JSONC configuration. By combining guards (conditional Bash expressions) with providers (external submenu generators), the system renders entries that react instantly to hardware detection, package presence, and user settings without blocking the UI thread.

Declarative Menu Entries in JSONC

The foundation of the Omarchy menu system resides in default/omarchy/omarchy-menu.jsonc, which describes a tree of items. Each entry supports optional fields that control visibility, state, and data source:

  • when — A Bash expression that determines row visibility. If the expression exits with a non-zero status, the entry is hidden.
  • checked — A Bash expression that, when true, renders a ✓ marker next to the entry.
  • disabled — A Bash expression that, when true, dims the row and disables cursor selection.
  • provider — The name of an external binary that supplies a submenu dynamically (e.g., listing installed applications).

Guard Evaluation for Conditional Entries

Guards enable context-aware menus by evaluating system state via Bash. All three guard types—when, checked, and disabled—are processed through the same pipeline but write different result tags (w, c, and d respectively) to distinguish their effects.

The Batch Guard Execution Flow

When a menu opens, the system batches all guard evaluations into a single subprocess to prevent UI blocking:

  1. Collect guards — MenuModel.guardScript(root.items) walks the item list and constructs a Bash script that prints lines formatted as <id>:w:1, <id>:c:0, or <id>:d:1 for each guard condition.

  2. Execute script — The guardProc Process element in shell/plugins/menu/Menu.qml runs the script via bash -lc <script>, capturing stdout line-by-line.

  3. Parse results — On process exit, the output splits into three maps: whenResults, checkedResults, and disabledResults.

  4. Rebuild UI — If the menu remains open (root.opened), root.rebuildDisplay() applies the corresponding CSS classes (hidden, checked, disabled) based on the result maps.

The evaluation is fully batched; a guardsPending flag prevents new evaluations from starting while a subprocess is active, ensuring the menu never blocks on individual guard commands.

Dynamic Submenus via Providers

While guards filter and style static entries, providers inject entire submenus dynamically. A provider is any binary implementing the MenuProvider IPC contract.

Provider Integration Flow

  1. Register provider — In shell/plugins/menu/Menu.qml, the providers property maps names to binaries:

    property var providers: {
        apps: "omarchy-menu-apps",
        "my-apps": "omarchy-menu-my-apps"
    }
  2. Reference in JSONC — An entry points to the provider via the provider field:

    "my.apps": {
        "icon":"󰀻",
        "label":"My Apps",
        "provider":"my-apps"
    }
  3. Load and merge — When selected, openExistingMenu(id) loads the provider's surface, reads its JSONC output, and merges rows into the current tree via mergeProviderRows.

  4. Live updates — Providers can emit a changed signal; the menu refreshes only the provider's rows without rebuilding the entire tree, maintaining performance.

Practical Configuration Examples

To add a conditional "Hybrid GPU" entry that appears only when hybrid graphics hardware is detected:

"trigger.hardware.hybrid-gpu": {
  "icon":"",
  "label":"Hybrid GPU",
  "when":"omarchy-hw-hybrid-gpu",
  "action":"omarchy-launch-floating-terminal-with-presentation omarchy-toggle-hybrid-gpu"
}

To register a custom provider in the QML layer, extend the providers map in shell/plugins/menu/Menu.qml:

property var providers: {
  apps: "omarchy-menu-apps",
  "my-apps": "omarchy-menu-my-apps"
}

The guard script generation logic in shell/plugins/menu/MenuModel.js collects conditions into a single evaluable string:

function guardScript(items) {
  let lines = []
  items.forEach(item => {
    if (item.when)    lines.push(`${item.id}:w:${item.when ? "1" : "0"}`);
    if (item.checked) lines.push(`${item.id}:c:${item.checked ? "1" : "0"}`);
    if (item.disabled)lines.push(`${item.id}:d:${item.disabled ? "1" : "0"}`);
  });
  return lines.length ? `for i in ${lines.join(' ')}; do eval "$i"; done` : "";
}

Summary

  • Configuration — Menu structure is declared in default/omarchy/omarchy-menu.jsonc using standard JSONC syntax with optional guard and provider fields.
  • Guard Batching — All conditional expressions are evaluated in a single subprocess via MenuModel.guardScript() and guardProc to prevent UI lag.
  • State Management — Guards control three distinct properties: when (visibility), checked (selection state), and disabled (interaction lock).
  • Provider Architecture — External binaries registered in Menu.qml supply dynamic submenus through the MenuProvider IPC contract, merged via mergeProviderRows.
  • Performance — Guard results and provider updates are cached and applied incrementally, ensuring the Omarchy menu system remains responsive during rapid system state changes.

Frequently Asked Questions

How does Omarchy handle multiple guards without slowing down the menu?

The system batches all guard evaluations into a single Bash script executed by guardProc. This subprocess runs asynchronously, and the UI updates only after all results are parsed into whenResults, checkedResults, and disabledResults. A guardsPending flag prevents redundant executions if the menu changes rapidly.

Can I create custom providers for the Omarchy menu system?

Yes. Any binary implementing the MenuProvider IPC contract can supply submenu data. Register the provider in the providers map inside shell/plugins/menu/Menu.qml, then reference it via the provider field in your JSONC entry. The menu calls openExistingMenu() to load your provider and merges its output using mergeProviderRows.

What happens if a guard Bash expression returns an error?

For when expressions, a non-zero exit status (including errors) causes the entry to be hidden. For checked and disabled, the specific guard type determines the default fallback state, but generally, a failed expression evaluates as false (unchecked or enabled), ensuring the menu remains functional even when hardware detection scripts fail.

Where is the menu configuration stored and how is it parsed?

The primary definition lives at default/omarchy/omarchy-menu.jsonc. The menu QML watches this file for changes. The JSONC structure supports comments and defines a tree where each node may contain when, checked, disabled, or provider fields that the system evaluates at runtime.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →