# How to Create a New Bar Widget Plugin for the Omarchy Shell

> Learn to create a new bar widget plugin for the Omarchy shell. Add a QML file, JavaScript model, and manifest to register your custom widget with the shell.

- Repository: [Omacom/omarchy](https://github.com/omacom/omarchy)
- Tags: how-to-guide
- Published: 2026-09-13

---

**Create a new bar widget plugin by adding a QML file that inherits from `BarWidget`, an optional JavaScript model for business logic, and a manifest file that registers the widget with the shell.**

The Omarchy shell (from the `omacom/omarchy` repository) renders its status bar through a Quickshell plugin system where each component is a self-contained QML module. To create a new bar widget plugin, you implement a declarative UI component, bundle it with a JSON manifest, and reference it in your user configuration.

## Overview of the Bar Widget Architecture

Omarchy’s status bar is implemented as the `omarchy.bar` Quickshell plugin. Each widget is a QML component backed by an optional JavaScript model, discovered at runtime through a `*.manifest.json` file. The loading logic resides in [`shell/plugins/bar/BarModel.js`](https://github.com/omacom/omarchy/blob/main/shell/plugins/bar/BarModel.js), which resolves widget IDs from [`shell.json`](https://github.com/omacom/omarchy/blob/main/shell.json) into filesystem paths and instantiates them as children of the bar container.

The creation process follows four mandatory steps:

1.  Create a QML widget file (`MyWidget.qml`) under `shell/plugins/bar/widgets/`
2.  Optionally add a JavaScript model ([`MyWidgetModel.js`](https://github.com/omacom/omarchy/blob/main/MyWidgetModel.js)) for non-UI logic
3.  Create a manifest ([`MyWidget.manifest.json`](https://github.com/omacom/omarchy/blob/main/MyWidget.manifest.json)) declaring the widget’s ID and entry point
4.  Enable the plugin and add it to the bar layout in `~/.config/omarchy/shell.json`

## Step 1: Create the QML Widget File

Create a new file under `shell/plugins/bar/widgets/`. The root element must be **BarWidget**, a base type defined in `shell/plugins/bar/Bar.qml` that injects required properties including `bar`, `moduleName`, and `settings`.

```qml
// shell/plugins/bar/widgets/MyWidget.qml
import QtQuick
import Quickshell
import qs.Commons
import "MyWidgetModel.js" as MyWidgetModel

BarWidget {
    id: root
    moduleName: "omarchy.my-widget"

    property int counter: 0

    WidgetButton {
        anchors.fill: parent
        bar: root.bar
        text: "MY"
        tooltipText: "Clicked " + root.counter + " times"
        onPressed: root.counter++
    }
}

```

**Why `BarWidget`?** All first-party widgets inherit from this base class. It provides the standardized interface that [`BarModel.js`](https://github.com/omacom/omarchy/blob/main/BarModel.js) expects when constructing the layout tree, ensuring your widget receives the correct shell context and settings object.

## Step 2: Add the JavaScript Model (Optional)

If your widget interacts with system state or performs complex data processing, isolate that logic in a sibling JavaScript file. Import it into your QML using the standard QtQuick module import syntax.

```javascript
// shell/plugins/bar/widgets/MyWidgetModel.js
function formatNumber(n) {
    return String(n).padStart(2, "0");
}

module.exports = { formatNumber };

```

Reference the model in your QML:

```qml
import "MyWidgetModel.js" as MyWidgetModel

// Inside a component:
text: MyWidgetModel.formatNumber(root.counter)

```

This pattern keeps UI declaration separate from business logic, matching the architecture used by built-in widgets like `KeyboardLayout` (see [`KeyboardLayoutModel.js`](https://github.com/omacom/omarchy/blob/main/KeyboardLayoutModel.js) for a production example).

## Step 3: Register the Widget with a Manifest

Every widget requires a **manifest** file that declares its `kind` (always `"bar-widget"` for bar components) and its entry QML file.

Create [`MyWidget.manifest.json`](https://github.com/omacom/omarchy/blob/main/MyWidget.manifest.json) in the same directory as your QML file:

```json
{
  "id": "omarchy.my-widget",
  "kind": "bar-widget",
  "entry": "MyWidget.qml"
}

```

**Critical constraints:**
- The `id` must be globally unique across the Omarchy ecosystem (use reverse-domain notation).
- The `entry` path is relative to the manifest’s directory.
- Do not edit the bar’s own [`manifest.json`](https://github.com/omacom/omarchy/blob/main/manifest.json); the shell discovers your widget automatically when the manifest is present in `shell/plugins/bar/widgets/`.

## Step 4: Enable and Configure the Widget

Enable the plugin using the Omarchy CLI:

```bash
omarchy plugin enable omarchy.my-widget

```

Then define its position in the bar by editing `~/.config/omarchy/shell.json`:

```json
{
  "version": 1,
  "bar": {
    "layout": {
      "right": [
        { "id": "omarchy.tray" },
        { "id": "omarchy.my-widget" }
      ]
    }
  }
}

```

The bar monitors this configuration file in real time; the widget appears immediately upon saving. Alternatively, use the CLI to automate placement:

```bash
omarchy bar move right omarchy.my-widget

```

## How the Bar Loads Custom Widgets

When the shell initializes, [`BarModel.js`](https://github.com/omacom/omarchy/blob/main/BarModel.js) reads the `bar.layout` object and constructs a layout tree. For each entry, the model invokes `customModulePath(entry, home, configDir)` to resolve the widget’s filesystem location. For first-party widgets with manifests, this resolves to `widgets/MyWidget.qml`.

Key functions in [`shell/plugins/bar/BarModel.js`](https://github.com/omacom/omarchy/blob/main/shell/plugins/bar/BarModel.js) governing this behavior:

- **`customModuleType(entry)`** – Determines whether a layout entry references a command or a QML module.
- **`customModulePath(entry, home, configDir)`** – Resolves the absolute path to the widget’s QML file based on the manifest ID.
- **`inlineSettingsDelta(current, next)`** – Optimizes live configuration updates, allowing the bar to update widget properties without rebuilding the entire layout.

## Complete Minimal Widget Example

The following file tree represents a deployable widget:

```

shell/
└─ plugins/
   └─ bar/
      └─ widgets/
         ├─ MyWidget.qml
         ├─ MyWidget.manifest.json
         └─ MyWidgetModel.js

```

**MyWidget.qml:**

```qml
import QtQuick
import Quickshell
import qs.Commons
import "MyWidgetModel.js" as MyWidgetModel

BarWidget {
    id: root
    moduleName: "omarchy.my-widget"
    property int counter: 0

    WidgetButton {
        anchors.fill: parent
        bar: root.bar
        text: "MY"
        tooltipText: "Clicked " + root.counter + " times"
        onPressed: root.counter = MyWidgetModel.double(root.counter)
    }
}

```

**MyWidget.manifest.json:**

```json
{
  "id": "omarchy.my-widget",
  "kind": "bar-widget",
  "entry": "MyWidget.qml"
}

```

**MyWidgetModel.js:**

```javascript
function double(n) { return n * 2; }
module.exports = { double };

```

Install the widget by copying these files to `shell/plugins/bar/widgets/`, then enable and position it using the commands in Step 4.

## Summary

- **Inherit from `BarWidget`** in `shell/plugins/bar/Bar.qml` to receive required shell context and properties.
- **Isolate logic** in an optional JavaScript model imported into your QML.
- **Declare the manifest** with a unique ID, kind `"bar-widget"`, and relative entry path.
- **Enable via CLI** with `omarchy plugin enable <id>` and position via [`shell.json`](https://github.com/omacom/omarchy/blob/main/shell.json) or `omarchy bar move`.
- **Loader resolution** happens in [`BarModel.js`](https://github.com/omacom/omarchy/blob/main/BarModel.js) through `customModulePath()`, supporting live reloads without shell restart.

## Frequently Asked Questions

### What is the required directory structure for a new bar widget?

Place all widget files under `shell/plugins/bar/widgets/` within the Omarchy repository or your local configuration directory. The QML file, manifest, and optional JavaScript model must share the same base filename (e.g., `Clock.qml`, [`Clock.manifest.json`](https://github.com/omacom/omarchy/blob/main/Clock.manifest.json), [`ClockModel.js`](https://github.com/omacom/omarchy/blob/main/ClockModel.js)) to ensure the loader in [`BarModel.js`](https://github.com/omacom/omarchy/blob/main/BarModel.js) resolves dependencies correctly.

### Can I update a widgets code without restarting the Omarchy shell?

Yes. The bar configuration is monitored in real time. Changes to `~/.config/omarchy/shell.json` apply instantly. However, modifications to the QML or JavaScript source files typically require a shell restart unless the widget explicitly implements hot-reloading logic using Quickshell’s dynamic instantiation APIs.

### What is the difference between `kind: "bar-widget"` and custom command modules?

The `"bar-widget"` kind indicates a native QML component that inherits from `BarWidget` and is discovered via a manifest file. Custom command modules (referenced directly in [`shell.json`](https://github.com/omacom/omarchy/blob/main/shell.json) without a manifest) execute external processes or load arbitrary QML files that do not necessarily inherit from the standard base class, bypassing the manifest discovery system in [`BarModel.js`](https://github.com/omacom/omarchy/blob/main/BarModel.js).

### How do I access user settings from within my widget?

The `BarWidget` base class automatically injects a `settings` object into your QML context. Access it via `root.settings` (where `root` is your widget’s ID). This object contains the merged configuration from [`shell.json`](https://github.com/omacom/omarchy/blob/main/shell.json) and responds to live updates processed by `inlineSettingsDelta()` in [`BarModel.js`](https://github.com/omacom/omarchy/blob/main/BarModel.js).