How the Omarchy Menu System Defines Entries and Providers in `omarchy-menu.jsonc`

The Omarchy menu system uses a hierarchical JSONC configuration file where dotted keys define menu paths, and each entry can specify static commands, conditional guards, or delegate to JavaScript provider functions that generate submenu items at runtime.

The Omarchy desktop environment implements a flexible, data-driven menu architecture through a single configuration file. Located in the basecamp/omarchy repository, this system allows users and developers to declare application launchers, system controls, and dynamic submenus using declarative syntax that supports hot-reloading and user-level extensions.

Structure of the omarchy-menu.jsonc File

The core menu definition resides at default/omarchy/omarchy-menu.jsonc. This file contains a single JSONC object where keys are dotted identifiers that implicitly create the menu hierarchy. For example, a key named personal.notes automatically establishes a personal parent node containing a notes child, eliminating the need to manually declare intermediate containers.

At runtime, the shell merges this default configuration with user extensions found at ~/.config/omarchy/extensions/omarchy-menu.jsonc, allowing local overrides without modifying core repository files.

Core Entry Properties

Each menu entry object supports the following properties:

  • label (string): The display text shown in the menu interface.
  • icon (string, optional): An icon identifier from the Omarchy icon font.
  • command (string, optional): The shell command executed when the entry is activated.
  • provider (string, optional): The name of a JavaScript function that returns dynamic submenu items.
  • guard (string, optional): A JavaScript expression evaluated to determine entry visibility.
  • children (object, optional): Inline nested menu definitions for static sub-trees.

Static Commands vs Dynamic Providers

The menu system distinguishes between static entries that execute fixed commands and dynamic entries that populate their submenus at runtime through provider functions.

Defining Static Menu Entries

Static entries map directly to executable actions. These are ideal for launching applications or triggering system commands.

{
  "system.shutdown": {
    "label": "Shutdown",
    "icon": "power",
    "command": "systemctl poweroff"
  },
  "apps.browser": {
    "label": "Web Browser",
    "icon": "firefox",
    "command": "firefox"
  }
}

When a user selects the Shutdown entry, the shell executes systemctl poweroff immediately.

Implementing Provider Functions

Providers enable dynamic content generation. Defined as JavaScript functions in shell/plugins/menu/providers/, these modules export functions that receive a context object and return an array of entry objects conforming to the same schema as static definitions.

// shell/plugins/menu/providers/runningAppsProvider.js
export function runningAppsProvider(context) {
  const apps = getRunningApps(); // Platform-specific helper
  return apps.map(app => ({
    label: app.name,
    command: `focus-app ${app.id}`,
    icon: "application"
  }));
}

To invoke a provider, reference its exported function name in the provider field:

{
  "apps.running": {
    "label": "Running Applications",
    "icon": "apps",
    "provider": "runningAppsProvider"
  }
}

When the user expands Running Applications, the shell invokes runningAppsProvider, caches the returned array, and renders the dynamic items as a submenu.

Conditional Visibility with Guards

Guards allow conditional entry visibility based on runtime state. These are JavaScript expressions evaluated in the same context as providers. If a guard evaluates to a falsy value, the entry is omitted from the rendered menu.

{
  "hardware.nvidia": {
    "label": "NVIDIA Settings",
    "command": "nvidia-settings",
    "guard": "hw.nvidiaPresent"
  }
}

In this example, the NVIDIA Settings entry only appears when the hw.nvidiaPresent helper reports a compatible GPU. Guards can reference any context variables available to providers, making them suitable for hardware detection, package availability checks, or user preference validation.

Loading Order and Runtime Behavior

The menu system follows a specific initialization sequence implemented in shell/plugins/menu/Menu.qml:

  1. Default Loading: The parser reads default/omarchy/omarchy-menu.jsonc into an internal object tree.
  2. Extension Merging: User configuration from ~/.config/omarchy/extensions/omarchy-menu.jsonc is deep-merged, allowing additions and overrides of existing dotted keys.
  3. Provider Registration: Provider functions are imported from shell/plugins/menu/providers/ and bound to their string identifiers.
  4. Hot-Reload: File system watchers monitor both JSONC files; any modification triggers a complete reparse and UI refresh without restarting the shell.

This architecture ensures that developers can iterate on menu configurations instantly, while end-users can extend functionality safely without touching repository files.

Summary

  • The Omarchy menu system centralizes configuration in omarchy-menu.jsonc, using dotted keys to define implicit hierarchies.
  • Static entries specify command strings for immediate execution, while dynamic entries reference JavaScript providers that return submenu items at runtime.
  • Guards are JavaScript expressions that conditionally hide entries based on hardware, software, or user context.
  • User extensions at ~/.config/omarchy/extensions/omarchy-menu.jsonc are merged with defaults, enabling safe customization.
  • The system supports hot-reloading via shell/plugins/menu/Menu.qml, applying changes immediately upon file save.

Frequently Asked Questions

What is the difference between a command and a provider?

A command is a static shell string executed immediately when the user selects the entry. A provider is a JavaScript function that returns an array of entry objects, enabling dynamic content generation such as listing recent files, running processes, or available AI models. Providers are evaluated only when their parent menu opens, ensuring lazy initialization of expensive operations.

How do I add a custom provider to Omarchy?

Create a new JavaScript file in shell/plugins/menu/providers/ that exports a function returning an array of entry objects. The function receives a context object containing platform helpers. Then reference the function name in the provider field of your omarchy-menu.jsonc entry. Ensure the filename matches the function name for clarity, though the system resolves providers by their export name rather than filename.

Why is my menu entry not appearing?

Check the guard expression if defined; the entry is hidden when the guard evaluates to falsy. Verify that the dotted key path is correct and that you have not accidentally overwritten the entry in a user extension. For provider-based entries, inspect the browser console or shell logs for JavaScript errors in your provider function, as runtime exceptions will cause the submenu to render empty.

Can I override default menu entries without editing core files?

Yes. Create or edit ~/.config/omarchy/extensions/omarchy-menu.jsonc and define an entry with the same dotted key as the default you wish to override. The merge process deep-merges objects at matching keys, allowing you to redefine properties like label, command, or provider while inheriting unspecified properties from the default configuration.

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 →