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:
-
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:1for each guard condition. -
Execute script — The
guardProcProcess element inshell/plugins/menu/Menu.qmlruns the script viabash -lc <script>, capturing stdout line-by-line. -
Parse results — On process exit, the output splits into three maps:
whenResults,checkedResults, anddisabledResults. -
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
-
Register provider — In
shell/plugins/menu/Menu.qml, theprovidersproperty maps names to binaries:property var providers: { apps: "omarchy-menu-apps", "my-apps": "omarchy-menu-my-apps" } -
Reference in JSONC — An entry points to the provider via the
providerfield:"my.apps": { "icon":"", "label":"My Apps", "provider":"my-apps" } -
Load and merge — When selected,
openExistingMenu(id)loads the provider's surface, reads its JSONC output, and merges rows into the current tree viamergeProviderRows. -
Live updates — Providers can emit a
changedsignal; 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.jsoncusing standard JSONC syntax with optional guard and provider fields. - Guard Batching — All conditional expressions are evaluated in a single subprocess via
MenuModel.guardScript()andguardProcto prevent UI lag. - State Management — Guards control three distinct properties:
when(visibility),checked(selection state), anddisabled(interaction lock). - Provider Architecture — External binaries registered in
Menu.qmlsupply dynamic submenus through the MenuProvider IPC contract, merged viamergeProviderRows. - 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →