Omarchy Quickshell Plugin System: Architecture, Discovery, and Runtime Loading
The Omarchy Quickshell plugin system loads first-party and third-party plugins at runtime via PluginRegistry.qml, using JSON manifests to register bar widgets, panels, and services into a reactive QML shell environment.
The Omarchy Quickshell plugin system provides a declarative extension mechanism for the omacom/omarchy desktop shell, allowing developers to inject custom UI components and background services without modifying core source code. Built on Qt's QML framework, the system differentiates between bundled first-party plugins located in shell/plugins/ and user-installed third-party plugins stored in ~/.config/omarchy/plugins. Each plugin exposes its capabilities through a validated manifest.json file that declares entry points, versioning, and widget metadata.
Plugin Manifest Structure and Validation
Every Omarchy Quickshell plugin requires a manifest.json (or *.manifest.json) that describes its runtime contract. The manifest must declare a unique id (e.g., omarchy.clock), a human-readable name, a semantic version, and a kinds array that categorizes the plugin's functionality (e.g., ["bar-widget"], ["service"], ["panel"]).
The entryPoints field maps each kind to a relative path pointing to the implementing QML component. For bar widgets, an optional barWidget object specifies default placement metadata such as the defaultSection (left, center, or right).
Validation occurs in shell/services/PluginRegistry.qml, which enforces that manifests are plain objects with required fields and that all entryPoints are safe relative paths containing no .. sequences or leading slashes. Upon successful validation, the registry injects internal stamps __sourceDir and __isFirstParty to distinguish built-in components from user installations.
Core Registry Components
The plugin system relies on three primary QML singletons that manage lifecycle and UI integration:
-
PluginRegistry(shell/services/PluginRegistry.qml): The central authority that scans plugin directories, maintains theinstalledPluginsmodel, and exposes imperative APIs includingsetEnabled(id, value, placement?),moveBarWidget, andputBarWidget. It handles URL resolution for entry points and manages thedisabledPluginslist for first-party components. -
BarWidgetRegistry(shell/services/BarWidgetRegistry.qml): A specialized registry that maintains the catalogue of available bar widgets. Plugins register their UI components viaregister(id, component, metadata), storing them in thewidgetsproperty for instantiation by the bar layout engine. -
ShellRoot(shell/shell.qml): The bootstrap component that instantiates both registries, wires them to configuration providers (shellConfigProvider,shellConfigMutator), and triggers the initial plugin scan at application startup.
Plugin Discovery and Directory Scanning
Plugin discovery follows a strict two-phase scanning process controlled by PluginRegistry:
-
Directory Preparation:
PluginRegistry.ensureUserDir()creates~/.config/omarchy/pluginsif absent, ensuring a valid target for third-party installations. -
Rescan Execution:
PluginRegistry.rescan()spawns a Bash script that walks the first-party directory (shell/plugins) and the third-party directory (~/.config/omarchy/plugins) one level deep. The script emits delimited output streams formatted as===firstparty::/path/to/plugin=== … JSON … === EOM ===.
PluginRegistry.parseScanOutput() parses this stream, validates each manifest, and merges results into the installedPlugins property. The registry also initializes localPluginWatcher, which uses inotifywait to monitor the user plugin directory for additions, removals, or modifications, triggering automatic rescans when filesystem changes occur.
Enabling and Disabling Plugins
The canonical API for toggling plugin state is PluginRegistry.setEnabled(id, value, placement?), which mutates the runtime configuration based on plugin kind:
-
Bar Widgets (
kindscontains"bar-widget"): Insert or remove entries fromshellConfig.bar.layoutwithin specific sections (left, center, right). The optionalplacementparameter acceptsbefore,after,section, orindexto control positioning relative to existing widgets. -
Bar Containers (
kindscontains"bar"): Toggle theshellConfig.bar.idproperty to switch between different bar implementations. -
Services, Panels, and Overlays: Add or remove plugin IDs from the top-level
plugins[]array in the configuration object.
For first-party plugins, disabling adds the ID to disabledPlugins[] rather than removing the entry entirely, preserving the ability to re-enable bundled components without filesystem lookup.
Bar Widget Registration and Placement
When a bar-widget plugin loads, its QML component executes registration against the widget catalogue:
BarWidgetRegistry.register("omarchy.clock", ClockComponent, { defaultSection: "center" })
This call, typically found in the plugin's Panel.qml or Service.qml, stores the component reference and metadata in BarWidgetRegistry.widgets. The bar UI iterates over BarWidgetRegistry.availableIds() to instantiate visible widgets according to the current layout configuration.
Developers can programmatically reposition widgets using moveBarWidget:
registry.moveBarWidget("omarchy.clock", {
before: "omarchy.audio",
section: "right"
})
Configuration and Runtime Updates
User configuration persists in ~/.config/omarchy/shell.json, a JSON file that the ShellRoot monitors for changes. A minimal configuration enabling a third-party plugin appears as:
{
"version": 1,
"plugins": [{ "id": "my.custom.plugin" }]
}
For precise bar widget placement, the configuration supports relational positioning:
{
"version": 1,
"bar": {
"layout": {
"right": [{ "id": "my.custom.widget", "before": "omarchy.tray" }]
}
}
}
When shell.json changes, ShellRoot increments registryRevision and emits pluginsChanged(), causing the registries to re-evaluate enabled states and the UI to refresh reactive bindings.
Summary
- Manifest-driven architecture: Plugins declare capabilities via
manifest.jsonwith validatedentryPointsandkindsarrays. - Dual source support: The system loads first-party plugins from
shell/plugins/and third-party plugins from~/.config/omarchy/plugins. - Centralized registries:
PluginRegistry.qmlhandles discovery and lifecycle, whileBarWidgetRegistry.qmlmanages UI component catalogues. - Dynamic enabling: The
setEnabled()API supports granular control over bar widgets, services, and panels with optional placement metadata. - Reactive configuration: Changes to
~/.config/omarchy/shell.jsontrigger immediate UI updates via filesystem monitoring and QML property binding.
Frequently Asked Questions
How does Omarchy Quickshell validate plugin manifests?
PluginRegistry.qml validates that each manifest.json is a plain object containing required fields (id, name, version, kinds, entryPoints) and ensures all entry point paths are relative without directory traversal sequences (.. or leading /). Invalid manifests are rejected during the scan phase.
Can I install third-party plugins without modifying the core repository?
Yes. Place any third-party plugin directory containing a valid manifest.json into ~/.config/omarchy/plugins. The PluginRegistry automatically detects these via directory scanning and inotifywait monitoring, making them available for enabling through the configuration API or shell.json edits.
What happens when I disable a first-party plugin versus a third-party plugin?
Disabling a first-party plugin adds its ID to the disabledPlugins[] array while retaining the manifest in memory, allowing immediate re-enabling without rescanning. Disabling a third-party plugin removes its entry from the active configuration entirely, requiring a subsequent rescan if the plugin files remain in the user directory.
How do I programmatically reposition a bar widget in the Omarchy Quickshell?
Use PluginRegistry.moveBarWidget(id, placement) where the placement object specifies section (left, center, right) and positional hints such as before or after referencing other widget IDs. This mutates the shellConfig.bar.layout and triggers immediate UI recomposition.
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 →