How to Enable Third-Party Plugins in Omarchy's shell.json
Third-party plugins are enabled by adding descriptor objects to the plugins array in $OMARCHY_PATH/config/omarchy/shell.json, which Quickshell reads at startup to dynamically load QML components from the config/omarchy/extensions/ directory.
Omarchy's desktop environment relies on Quickshell to render its interface, with all plugin registration centralized in a single JSON configuration file. Understanding how to modify shell.json allows you to extend the desktop with custom widgets, indicators, and utilities without altering core source files. This guide walks through the exact steps to register external plugins, the structure of plugin descriptors, and how the loader in shell/shell.qml instantiates each component.
How the Plugins Array Works in shell.json
At the root of Omarchy's configuration file, shell.json contains a top-level plugins property that defaults to an empty array. This array acts as a manifest that tells Quickshell which external QML files to instantiate alongside the built-in UI elements.
According to the basecamp/omarchy source code, when the plugins array contains entries, the loader iterates over shellConfig.plugins during initialization. Each entry in the array represents a third-party component that will be loaded from the config/omarchy/extensions/ directory hierarchy.
Enabling a Third-Party Plugin: Step-by-Step
To activate a third-party plugin in Omarchy, you need to place the QML files in the correct location and register them in the configuration.
-
Place the plugin files into the
config/omarchy/extensions/directory within your Omarchy installation path. -
Edit
shell.jsonand add a descriptor object to thepluginsarray. The object must include at least anidfield that matches your plugin's QML component identifier. -
Save the configuration and reload your Quickshell session. The watcher on
shell.jsonpicks up changes automatically on the next start.
The loader uses "$OMARCHY_PATH/config/omarchy/extensions" as its base path, so plugins placed there can be referenced by filename alone.
Plugin Descriptor Format
Each entry in the plugins array is a JSON object with specific keys that control loading behavior:
id(required): The identifier used throughout the UI to reference the plugin (e.g.,omarchy.my.custom-plugin).path(optional): The relative path to the QML file within the extensions directory. If omitted, the loader assumes the file is named<id>.qml.config(optional): A JSON object passed to the plugin'sComponent.onCompletedhandler, allowing runtime customization without code changes.
A typical descriptor looks like this:
{
"id": "my.custom-plugin",
"path": "my-plugin.qml",
"config": {
"refreshInterval": 60,
"showIcon": true
}
}
How Quickshell Loads Plugins
The dynamic loading mechanism lives in shell/shell.qml, which implements the plugin instantiation logic. According to the basecamp/omarchy source code, the loader first reads shell.json via Qt.openUrlExternally("$OMARCHY_PATH/config/omarchy/shell.json") and processes the plugins array during initialization.
For each entry in shellConfig.plugins, the loader builds a component using Qt.createComponent("$OMARCHY_PATH/config/omarchy/extensions/" + entry.path)—or assumes the filename is <id>.qml if path is omitted. The resulting instance is added to the main QML scene, exposing the plugin's exported properties and signals to Omarchy's bar, indicators, and other built-in modules.
This architecture ensures that third-party code runs in the same context as native UI elements while maintaining isolation through the QML component model.
Practical Example: Adding a Custom Clock Plugin
Here is a complete workflow for adding a simple clock extension to Omarchy:
Step 1: Create config/omarchy/extensions/extra-clock.qml with the following content:
import QtQuick 2.15
import Quickshell 1.0
Item {
Text {
id: clock
font.pixelSize: 24
color: "white"
text: Qt.formatDateTime(new Date(), "hh:mm:ss")
Timer {
interval: 1000; running: true; repeat: true
onTriggered: clock.text = Qt.formatDateTime(new Date(), "hh:mm:ss")
}
}
}
Step 2: Add the descriptor to config/omarchy/shell.json:
{
"id": "omarchy.extra-clock",
"path": "extra-clock.qml"
}
Step 3: Save the file and reload your Quickshell session. The extra-clock component will now instantiate alongside the default shell components.
Summary
- Configuration file: Third-party plugins are registered in
$OMARCHY_PATH/config/omarchy/shell.jsonvia thepluginsarray. - Location: Place plugin QML files in
config/omarchy/extensions/to ensure the loader can resolve paths. - Descriptor requirements: Each plugin needs at minimum an
idfield; optionalpathandconfigkeys customize loading behavior. - Loading mechanism:
shell/shell.qmlreads the array viaQt.openUrlExternally()and creates components dynamically usingQt.createComponent(). - Hot-reloading: Changes to
shell.jsontake effect on the next Quickshell session start.
Frequently Asked Questions
What happens if the plugins array is empty?
When the plugins array is empty or omitted, Quickshell loads only the built-in UI elements defined in shell/shell.qml. The desktop environment runs with default functionality, and no third-party components are instantiated.
Can I pass configuration data to a plugin without modifying its QML source?
Yes. Include a config object in the plugin descriptor within shell.json. This JSON object is passed to the plugin's Component.onCompleted handler, allowing you to adjust behavior such as refresh intervals, display options, or API endpoints without touching the source code.
Where should I place third-party plugin files in the Omarchy repository?
Place all third-party QML files and their associated resources (icons, scripts, etc.) in the config/omarchy/extensions/ directory. The loader in shell/shell.qml uses this path as the base directory when resolving Qt.createComponent() calls, ensuring consistent module loading.
Does Omarchy support hot-reloading of plugins without restarting the session?
While Quickshell watches shell.json for changes, new plugins are typically instantiated on the next session start or manual reload. The configuration file is monitored, but third-party QML components require a fresh component creation cycle initiated by the loader in shell/shell.qml.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →