How to Create Custom Bar Modules in the Omarchy Shell: A Complete Guide

You can create custom bar modules in the Omarchy shell by writing QML components that export a unique moduleName property, placing them in $HOME/.config/omarchy/plugins/, and registering them in shell.json via the BarWidgetRegistry system.

Omarchy’s top-bar is built from extensible bar widgets (modules) that require no recompilation to customize. The shell uses a dynamic registry system defined in shell/shell.qml to map module IDs to QML component factories, allowing you to drop in custom code that persists across upgrades. This guide explains how to leverage the BarWidgetRegistry and shell.json configuration to build and deploy custom bar modules.

Understanding the Bar Widget Architecture

The Omarchy shell loads bar widgets through a three-tier system involving the registry, QML component definitions, and JSON manifest entries.

The BarWidgetRegistry System

At startup, shell/shell.qml instantiates a central BarWidgetRegistry:

property BarWidgetRegistry barWidgetRegistry: BarWidgetRegistry { }

This registry maintains an internal map of module IDs (strings like "omarchy.mywidget") to QML component factories. When shell/plugins/bar/Bar.qml renders the top bar, it iterates over registry entries and injects each widget into a Repeater that populates the layout regions (left, center, right). The registry’s moduleWidgets(id) helper returns instantiated components for any given moduleName.

Widget Definition Requirements

Every bar module must be a QML file that exports a moduleName property:

property string moduleName: "omarchy.mywidget"

Built-in widgets reference shell/plugins/bar/widgets/Workspaces.qml as the canonical implementation. Custom widgets follow the same pattern but reside in user-space directories.

Manifest Configuration

The shell.json file (default location: config/omarchy/shell.json) stores the bar layout array. Each entry specifies:

  • id: The module ID matching the QML moduleName
  • type: Currently "qml" for all widgets
  • region: "left", "center", or "right"
  • order: Optional integer for placement priority within the region

Once you edit shell.json for the first time, it becomes the canonical configuration, persisting your custom modules independently of Omarchy upgrades.

Creating Your First Custom Bar Module

You can add a custom widget in three steps without modifying Omarchy core code.

Step 1: Write the QML Component

Create a new directory for your plugin and add a Widget.qml file:

// File: ~/.config/omarchy/plugins/mywidget/Widget.qml
import QtQuick 2.15
import QtQuick.Controls 2.15

Item {
    // Unique identifier – must match shell.json entry
    property string moduleName: "omarchy.mywidget"

    Text {
        text: new Date().toLocaleTimeString()
        font.bold: true
        color: Theme.accent
        anchors.centerIn: parent
    }
}

The moduleName property is required—it tells BarWidgetRegistry how to route the component.

Step 2: Register in shell.json

Add an entry to your user configuration at ~/.config/omarchy/shell.json under the bar.layout array:

{
  "id": "omarchy.mywidget",
  "type": "qml",
  "region": "right",
  "order": 5
}

The region controls horizontal placement, while order determines stacking priority within that region.

Step 3: Install Using the CLI

Use Omarchy’s CLI to register the plugin and enable the widget:


# Create plugin directory and copy files

omarchy plugin add mywidget ~/.config/omarchy/plugins/mywidget

# Enable the widget in the bar (writes to shell.json)

omarchy bar put omarchy.mywidget '{"region":"right","order":5}'

The omarchy bar put command automatically updates shell.json, eliminating manual JSON editing.

Advanced Widget Implementation

Beyond simple displays, you can build interactive controls using standard Qt Quick components.

Building Interactive Controls

Here is a clickable widget that triggers notifications:

Item {
    property string moduleName: "omarchy.clicker"
    width: 80
    height: 30
    
    Rectangle {
        anchors.fill: parent
        color: "#334455"
        radius: 4
        
        Text {
            anchors.centerIn: parent
            text: "Click!"
            color: "white"
        }
        
        MouseArea {
            anchors.fill: parent
            onClicked: omarchyNotificationSend("Clicked!", "You pressed the custom bar widget")
        }
    }
}

This pattern works because Bar.qml injects the widget into the scene graph like any native component.

Reference Implementation

Study shell/plugins/bar/widgets/Workspaces.qml in the Omarchy repository for a production-grade template. It demonstrates proper signal handling, theme integration, and layout constraints required for first-party widgets.

Runtime Management and Persistence

The Omarchy shell provides CLI tools for modifying bar layouts without restarting.

Modifying Layouts Dynamically

Use these commands to manage custom modules at runtime:

  • omarchy bar move <id> <region> – Relocates a widget to a different bar region
  • omarchy bar put <id> <json> – Adds or updates a widget’s configuration
  • omarchy plugin enable <name> – Activates a plugin directory

Changes reflect immediately because Bar.qml binds to the registry’s model, and shell.json persists your configuration.

Upgrade Safety

Because custom widgets live in $HOME/.config/omarchy/plugins/ and register via the user-specific shell.json, they survive Omarchy updates. The BarWidgetRegistry resolves module IDs at runtime, so core shell updates never overwrite your custom QML code.

Summary

  • No recompilation required: Drop QML files into ~/.config/omarchy/plugins/ and register them via shell.json.
  • Registry-based loading: shell/shell.qml creates a BarWidgetRegistry that maps moduleName strings to component factories.
  • CLI integration: Use omarchy bar put and omarchy plugin add to install widgets without manual JSON editing.
  • Persistent configuration: Once modified, shell.json becomes the canonical layout source, preserving custom modules across upgrades.
  • Reference code: Study shell/plugins/bar/widgets/Workspaces.qml for implementation patterns.

Frequently Asked Questions

Do I need to rebuild Omarchy to add custom bar widgets?

No. Omarchy’s BarWidgetRegistry loads QML components dynamically from shell/plugins/bar/widgets/ (built-in) or $HOME/.config/omarchy/plugins/ (custom). Because the registry resolves moduleName properties at runtime, you can add, remove, or modify widgets without restarting the shell or recompiling source code.

Where should I place third-party bar widget files?

Place custom QML files in $HOME/.config/omarchy/plugins/<plugin-name>/Widget.qml. The omarchy plugin add command copies directories into this location. Omarchy scans this path on startup alongside the built-in shell/plugins/bar/widgets/ directory, treating user plugins as first-class modules.

How does the shell determine which widgets appear in the bar?

shell/plugins/bar/Bar.qml reads the bar.layout array from shell.json. For each entry with "type": "qml", it queries BarWidgetRegistry.moduleWidgets(id) to instantiate the corresponding QML component. Only widgets with matching moduleName properties and active manifest entries render in the UI.

Can I change widget order without manually editing JSON?

Yes. Use the CLI command omarchy bar move <id> <region> to shift widgets between left, center, and right regions, or omarchy bar put <id> '{"order":N}' to adjust stacking priority. These commands modify shell.json atomically and trigger immediate UI updates through the registry’s change notifications.

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 →