How to Create Custom Omarchy Menu Extensions: A Complete Guide

Omarchy menu extensions are created by editing ~/.config/omarchy/extensions/omarchy-menu.jsonc, which gets merged with the default menu at runtime using the mergeMenuSources function in shell/plugins/menu/MenuModel.js.

Omarchy's command menu system is built on the omarchy.menu Quickshell plugin and offers a flexible, JSONC-based extension mechanism. This guide explains how to add custom entries, submenus, and conditional visibility to tailor the launcher to your workflow.


Understanding the Omarchy Menu Architecture

The menu structure is defined by two JSONC files that Omarchy merges when the shell starts:

  • default/omarchy/omarchy-menu.jsonc – The shipped default menu definition
  • ~/.config/omarchy/extensions/omarchy-menu.jsonc – Your custom extensions

The merge logic lives in shell/plugins/menu/MenuModel.js within the mergeMenuSources function. This overlay system works key-by-key:

  • Matching IDs receive partial updates (change only label or icon without touching action)
  • New IDs are appended to the menu tree
  • A synthetic root entry is injected when neither file defines one

If your extensions file doesn't exist, omarchy-refresh-config creates it automatically from the sample at config/omarchy/extensions/omarchy-menu.jsonc.


Entry Schema for Custom Omarchy Menu Extensions

Each menu entry supports these core fields:

Field Purpose
icon Nerd Font glyph displayed in the icon column
label Human-readable title (defaults to entry ID)
action Bash command executed when selected
target ID of another submenu to open (turns entry into a navigation link)
when Bash condition that hides the entry when false
checked Bash condition that shows a checkmark when true
disabled Bash condition that dims the entry when true

Guards (when, checked, disabled) are evaluated in a batched Bash process for performance. For dynamic content—like listing installed applications—point a submenu to a provider such as apps, fonts, or power-profiles. Providers are defined in shell/plugins/menu/Menu.qml.


Creating Your First Custom Menu Extension

Step 1: Locate or Create the Extensions File

Ensure the file exists at:

~/.config/omarchy/extensions/omarchy-menu.jsonc

If missing, run:

omarchy-refresh-config

Step 2: Add Entries Using Dotted-ID Notation

Use dot-separated IDs to place entries anywhere in the hierarchy. For example, personal.notes nests under personal.

Step 3: Apply Changes Instantly

Omarchy monitors the extensions file. Press Super + Space or run:

omarchy menu refresh

No shell restart required.


Practical Omarchy Menu Extension Examples

Add a Simple Launcher Entry

Open a terminal with Vim for quick notes:

{
  // ~/.config/omarchy/extensions/omarchy-menu.jsonc
  "personal": {
    "label": "Personal"
  },

  "personal.notes": {
    "icon": "\uf15c",
    "label": "Notes",
    "action": "omarchy launch-terminal \"vim ~/notes.txt\""
  }
}

The \uf15c is a Nerd Font "file-text" glyph.

Conditionally Show Entries Based on Package Installation

Display Syncthing UI only when the package is present:

{
  "personal.syncthing": {
    "icon": "\uf0e7",
    "label": "Syncthing UI",
    "action": "omarchy launch-browser http://localhost:8384",
    "when": "$(omarchy-pkg-present syncthing)"
  }
}

The omarchy-pkg-present utility returns exit code 0 only when the package is installed.

Create a Submenu with a Built-in Provider

Reuse Omarchy's application launcher as a submenu:

{
  "tools": {
    "label": "Tools",
    "provider": "apps"
  }
}

Controlling the Menu from CLI

The bin/omarchy-menu script provides programmatic access:

Command Effect
omarchy menu Toggle the root menu
omarchy menu toggle system Open/close the "system" submenu
omarchy menu summon style.theme Directly activate a specific entry
omarchy menu refresh Force re-parsing of all JSONC sources

These commands work from shell scripts, keybindings, or automation tools.


Testing Custom Omarchy Menu Extensions

Verify your extensions with the built-in test suite:

./test/shell

Specifically, test/shell.d/menu-test.sh validates JSONC parsing and merging, while test/shell.d/menu-guards-test.sh checks conditional visibility logic. Run these after adding complex guards to catch syntax errors early.


Summary

  • Custom Omarchy menu extensions live in ~/.config/omarchy/extensions/omarchy-menu.jsonc
  • The mergeMenuSources function in shell/plugins/menu/MenuModel.js overlays your entries onto defaults
  • Use dotted IDs (category.item) to position entries anywhere in the tree
  • Guards (when, checked, disabled) enable conditional visibility via Bash expressions
  • Providers (apps, fonts, etc.) generate dynamic submenu content
  • Changes apply instantly without restarting the shell
  • Test extensions with ./test/shell before committing to your dotfiles

Frequently Asked Questions

What happens if my custom entry uses the same ID as a default entry?

Only the fields you explicitly declare are replaced. You can retitle or re-icon a default entry without redefining its action. Omit action entirely to keep the original behavior while changing the label.

Can I remove or hide default menu entries?

Yes—override the entry with a when guard that always evaluates false: "when": "false". The entry disappears from the menu while preserving the original definition in default/omarchy/omarchy-menu.jsonc.

How do I find valid Nerd Font icon codes?

Browse nerdfonts.com/cheat-sheet for glyph codes. Prefix with \u and use four hexadecimal digits (e.g., \uf15c). Verify the glyph renders in your terminal before adding to the menu.

Do I need to restart Omarchy after editing the extensions file?

No. The omarchy-shell process watches ~/.config/omarchy/extensions/omarchy-menu.jsonc via filesystem notification. Use omarchy menu refresh if you suspect the watcher missed an update.

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 →