# How to Define and Structure Menus in Omarchy

> Learn to define and structure menus in Omarchy using declarative JSONC configuration. Build hierarchical menus rendered as a dmenu-style picker with Qt Quick.

- Repository: [37signals/omarchy](https://github.com/basecamp/omarchy)
- Tags: how-to-guide
- Published: 2026-08-26

---

**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.jsonc` contains the base installation's menu tree.
- **User extensions**: `$HOME/.config/omarchy/extensions/omarchy-menu.jsonc` overrides 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:

- `learn` creates a top-level "Learn" category.
- `learn.keybindings` places an item inside the Learn submenu.
- `trigger.share.file` nests 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-keybindings` or `omarchy-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:

```jsonc
{
  // 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:

```jsonc
{
  "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:

```bash

# 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:

1. Load `default/omarchy/omarchy-menu.jsonc` into the base tree.
2. If `~/.config/omarchy/extensions/omarchy-menu.jsonc` exists, overlay its keys onto the base.
3. Evaluate all `when` predicates and prune invisible entries.
4. 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.item` create the hierarchy automatically without nested objects.
- Each entry requires `icon`, `label`, and `action` fields, with optional `aliases` and conditional `when` predicates.
- The `shell/plugins/menu/Menu.qml` plugin merges configurations, evaluates conditions, and renders the UI.
- CLI tools such as `bin/omarchy-menu` and `omarchy-menu-select` provide 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.