# How to Declare Custom Bar Modules Inline in Omarchy's shell.json

> Learn to declare custom bar modules inline in Omarchy shell.json using command or qml types. Render modules at runtime without separate plugin registration.

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

---

**Yes, you can declare custom bar modules inline in Omarchy's [`shell.json`](https://github.com/basecamp/omarchy/blob/main/shell.json) by adding entries with `type: "command"` or `type: "qml"` to the `bar.layout` sections, which the [`BarModel.js`](https://github.com/basecamp/omarchy/blob/main/BarModel.js) parses and renders at runtime without requiring separate plugin registration.**

Omarchy is an open-source desktop environment developed by Basecamp that uses a JSON-driven configuration system for its shell components. You can extend the top bar with custom widgets by declaring them inline in your [`shell.json`](https://github.com/basecamp/omarchy/blob/main/shell.json) file, eliminating the need to create full plugin packages for simple utilities.

## Understanding the bar.layout Configuration Schema

The bar configuration lives under the `bar:` subtree in `~/.config/omarchy/shell.json` (or the bundled default at [`config/omarchy/shell.json`](https://github.com/basecamp/omarchy/blob/main/config/omarchy/shell.json)). The `bar.layout` object contains three sections—`left`, `center`, and `right`—each accepting an array of module definitions.

According to the Omarchy source code, when `shell.qml` loads the canonical configuration, it passes the parsed JSON object directly to the bar QML component. The [`shell/plugins/bar/BarModel.js`](https://github.com/basecamp/omarchy/blob/main/shell/plugins/bar/BarModel.js) file inspects each entry's `type` field to determine whether to instantiate a built-in widget or a custom module.

## Types of Inline Custom Modules

Omarchy recognizes two primary types for inline declaration, plus a settings override pattern for existing widgets.

### Command-Based Widgets (type: "command")

Use this pattern for simple scripts that output text. The [`BarModel.js`](https://github.com/basecamp/omarchy/blob/main/BarModel.js) creates a `customCommandModuleComponent` that spawns the specified process at the configured `interval` and displays its stdout.

```json
{
  "id": "custom.cpu",
  "type": "command",
  "exec": "bash -c \"printf 'CPU %s' \\$(grep -c '^processor' /proc/cpuinfo)\"",
  "interval": 30,
  "label": "CPU"
}

```

- `type: "command"` tells the bar to spawn a process
- `exec` contains the command line (wrap in `bash -c` for complex scripts)
- `interval` specifies the refresh rate in seconds
- `label` is optional; omit it to display the command's raw output

### QML-Based Widgets (type: "qml")

For custom visual elements, specify a path to a QML file. The model resolves the path via `customModulePath()` and loads it through the `customRoot` component in `Bar.qml`.

```json
{
  "id": "custom.clock",
  "type": "qml",
  "source": "~/.config/omarchy/custom-clock.qml",
  "label": "Clock"
}

```

- `type: "qml"` signals the bar to load a QML component
- `source` accepts absolute paths or `~`-expanded paths to your home directory
- The referenced QML file can contain any visual element, such as rotating logos or custom graphics

### Patching Built-in Widgets with Settings

You can modify existing widgets without fully redefining them by specifying their `id` and a `settings` object. The [`BarModel.js`](https://github.com/basecamp/omarchy/blob/main/BarModel.js) merges these settings into the running widget without rebuilding the entire bar.

```json
{
  "id": "omarchy.clock",
  "settings": {
    "format": "15:04",
    "showSeconds": false
  }
}

```

This approach updates the configuration while preserving the widget's core functionality.

## Runtime Processing and Hot-Reload

The inline declaration system relies on specific components in the Omarchy codebase:

- **[`shell/plugins/bar/BarModel.js`](https://github.com/basecamp/omarchy/blob/main/shell/plugins/bar/BarModel.js)** implements `customModuleSafeName()`, `customModuleType()`, and `customModulePath()` to validate names, determine module types, and resolve source paths
- **`shell/plugins/bar/Bar.qml`** instantiates either standard widgets or custom components based on the entry's `type` field
- **`shell/shell.qml`** handles the initial JSON parsing and enables configuration hot-reloading

When you save changes to [`shell.json`](https://github.com/basecamp/omarchy/blob/main/shell.json), run `omarchy reloadConfig` (or wait for auto-reload), and the bar rebuilds its layout using the updated inline definitions. No separate plugin registration is required, and the changes persist across restarts.

## Summary

- **Declare custom modules** directly in `~/.config/omarchy/shell.json` under `bar.layout.left`, `center`, or `right`
- **Use `type: "command"`** for shell script output widgets with configurable refresh intervals
- **Use `type: "qml"`** for custom visual components stored as separate QML files
- **Override existing widgets** by specifying their `id` with a `settings` object
- **Changes hot-reload** automatically or via `omarchy reloadConfig` without restarting the desktop environment

## Frequently Asked Questions

### Can I declare custom bar modules inline without creating a plugin package?

Yes. Omarchy's [`shell.json`](https://github.com/basecamp/omarchy/blob/main/shell.json) schema supports inline declaration for lightweight, one-off widgets. Simply add an entry with the appropriate `type` field to your `bar.layout` configuration. The [`BarModel.js`](https://github.com/basecamp/omarchy/blob/main/BarModel.js) processes these entries at runtime, so you do not need to create a full plugin package or register the module separately.

### What programming languages can I use for command-type modules?

You can use any programming language that executes in your shell environment. The `exec` field accepts standard shell commands, so you can write scripts in Bash, Python, Ruby, or any other language available in your `$PATH`. The bar captures stdout and displays it in the widget.

### Where should I store custom QML files for bar modules?

Store custom QML files in `~/.config/omarchy/` or any location accessible via absolute path. The `source` field in your [`shell.json`](https://github.com/basecamp/omarchy/blob/main/shell.json) entry supports tilde expansion (`~`), so `~/.config/omarchy/custom-widget.qml` resolves correctly to your home directory when the [`BarModel.js`](https://github.com/basecamp/omarchy/blob/main/BarModel.js) processes the `customModulePath`.

### Do I need to restart Omarchy after editing shell.json?

No. The `shell.qml` component monitors the configuration file for changes and hot-reloads the bar layout automatically. You can also trigger an immediate reload by running `omarchy reloadConfig` from a terminal. The bar reconstructs itself using the updated inline module definitions without requiring a full desktop restart.