How to Define and Structure Menus in Omarchy
Omarchy uses declarative JSONC configuration files with dotted identifiers to define hierarchical menu structures that the Qt Quick (QML) plugin renders as a dmenu-style picker at runtime.
Omarchy is a declarative desktop environment by Basecamp that replaces traditional application menus with a data-driven launcher system. Instead of hard-coding UI elements, you define and structure menus in Omarchy by editing JSONC files that the shell dynamically loads and merges at startup. This architecture separates presentation logic from menu content, allowing deep customization without modifying core source code.
Understanding the Menu Architecture
Omarchy's menu system is split between a default definition shipped with the shell and optional user extensions. The system relies on a flat key structure where dotted identifiers infer nesting levels, creating submenus automatically without deeply nested JSON objects.
Core Configuration Files
According to the source code in shell/plugins/menu/Menu.qml, the loader reads from two primary locations:
- Default menu:
$OMARCHY_PATH/default/omarchy/omarchy-menu.jsonccontains the base installation's menu tree. - User extensions:
$HOME/.config/omarchy/extensions/omarchy-menu.jsoncoverrides or appends to the default entries.
At startup, the QML plugin first parses the default file, then merges the user extension if present, with user values taking precedence. This merge strategy ensures system updates do not overwrite personal customizations.
Hierarchical Identifiers
Menu hierarchy is inferred from dot-separated keys rather than nested JSON objects. For example:
learncreates a top-level "Learn" category.learn.keybindingsplaces an item inside the Learn submenu.trigger.share.filenests three levels deep: Trigger > Share > File.
This flat structure keeps the JSONC readable while supporting unlimited nesting depth, as the parser splits each key on periods to build the tree model.
Structuring Menu Entries
Each menu entry is a JSONC object requiring three mandatory fields that the picker UI uses for display and execution.
Required Fields
Every entry must specify:
icon: A Unicode character or font glyph displayed beside the label (e.g.,"").label: The human-readable text shown in the picker.action: The shell command executed when selected (e.g.,omarchy-menu-keybindingsoromarchy-launch-firefox).
Optional Conditions and Aliases
Advanced entries support:
aliases: An array of alternative strings for fuzzy matching.when: A shell predicate that determines visibility; the entry appears only when the command returns exit code 0.
JSONC Syntax Benefits
The format accepts trailing commas and JavaScript-style comments, reducing syntax errors during iterative editing. This is particularly useful when extending ~/.config/omarchy/extensions/omarchy-menu.jsonc, as you can annotate sections without breaking the parser.
Practical Examples
Extending the Default Menu
Add a custom "Utilities" section with a system cleaner tool:
{
// Top-level category container
"utilities": {
"icon": "🧹",
"label": "Utilities",
"action": ""
},
// Nested item automatically placed under Utilities
"utilities.cleaner": {
"icon": "🗑️",
"label": "System Cleaner",
"action": "omarchy-cleaner",
"when": "command -v omarchy-cleaner"
}
}
Save this to ~/.config/omarchy/extensions/omarchy-menu.jsonc. The shell hot-reloads the file on save, or you can restart the session to force a refresh.
Creating a Conditional Entry
Show a VPN toggle only when the network manager is available:
{
"network.vpn": {
"icon": "🔒",
"label": "Toggle VPN",
"action": "nmcli con up vpn",
"when": "nmcli --version"
}
}
Using CLI Wrappers
The command-line wrappers are thin shims that forward arguments to the QML plugin, ensuring menu definitions remain independent of invocation method:
# Launch the main menu toggle
bin/omarchy-menu toggle
# Display a dynamic picker with specific options
omarchy-menu-select "Pick an app" "firefox" "kitty" "code"
# Pipe input for ad-hoc menus
echo -e "Option 1\nOption 2" | omarchy-menu-input
Runtime Loading and UI Rendering
The menu UI is decoupled from its data source through the QML plugin layer.
QML Plugin Implementation
The shell/plugins/menu/Menu.qml component handles parsing. It reads the JSONC files, evaluates when predicates in a subshell, and constructs the internal tree model passed to the dmenu-style renderer. This implementation ensures the UI updates immediately when the underlying JSONC changes.
Merging Logic
The loader applies a shallow merge strategy:
- Load
default/omarchy/omarchy-menu.jsoncinto the base tree. - If
~/.config/omarchy/extensions/omarchy-menu.jsoncexists, overlay its keys onto the base. - Evaluate all
whenpredicates and prune invisible entries. - Flatten dotted keys into the nested model for the UI delegate.
Summary
- Omarchy menus are defined in JSONC files located at
default/omarchy/omarchy-menu.jsonc(system) and~/.config/omarchy/extensions/omarchy-menu.jsonc(user). - Dotted identifiers like
category.subcategory.itemcreate the hierarchy automatically without nested objects. - Each entry requires
icon,label, andactionfields, with optionalaliasesand conditionalwhenpredicates. - The
shell/plugins/menu/Menu.qmlplugin merges configurations, evaluates conditions, and renders the UI. - CLI tools such as
bin/omarchy-menuandomarchy-menu-selectprovide shell access without hard-coding menu content.
Frequently Asked Questions
What file format does Omarchy use for menu definitions?
Omarchy uses JSONC (JSON with Comments), which supports trailing commas and JavaScript-style comments. The parser allows more flexible editing than strict JSON, making it ideal for user modifications in ~/.config/omarchy/extensions/omarchy-menu.jsonc.
How do I create a nested submenu in Omarchy?
Use dot notation in your key names. A key named productivity.writing.tools automatically creates a "Productivity" top-level menu containing a "Writing" submenu, which contains the "Tools" item. You do not need to nest JSON objects; the parser infers depth from the key string.
Can I show or hide menu items based on system state?
Yes. Add a when field containing a shell command. The entry appears only if the command returns exit code 0. For example, "when": "which docker" shows the item only when Docker is installed on the system.
Where should I place custom menu definitions?
Place user-specific overrides in ~/.config/omarchy/extensions/omarchy-menu.jsonc. This file is merged with the default menu at default/omarchy/omarchy-menu.jsonc during shell initialization, with user entries taking precedence. Never edit the system default file directly, as changes will be lost on updates.
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 →