How the Omarchy Plugin Registry Handles Discovery, Loading, and Hot Reload
The Omarchy plugin registry discovers plugins by scanning the shell/plugins/ directory for valid manifest.json files, dynamically loads them via QML loaders with injected configuration properties, and supports hot reloading through file system watchers that unload and re-instantiate components when source files change.
Omarchy is an open-source desktop shell built on Quickshell that uses a modular plugin architecture to manage panels, overlays, and system menus. The Omarchy plugin registry, implemented in shell/services/PluginRegistry.qml, serves as the central orchestrator that validates, instantiates, and manages the lifecycle of all shell extensions without requiring a full session restart.
Plugin Discovery and Validation
The registry begins its lifecycle by scanning the filesystem for valid plugin bundles.
Directory Scanning and Manifest Validation
According to the source code in shell/services/PluginRegistry.qml, the registry recursively scans the shell/plugins/ directory (and any additional paths defined in shell.json) for subdirectories containing a manifest.json file. For each discovered folder, the registry performs three validation steps:
- Manifest parsing – Reads the JSON to extract the plugin
name,type(panel, overlay, agent, or menu), andentryQML component path. - File existence check – Verifies that the
entryQML file exists inside the plugin directory before attempting to load it. - Enablement verification – Checks the user’s
~/.config/omarchy/shell.jsonto determine if the plugin is marked asenabled. Disabled plugins are tracked but excluded from the active set.
Only plugins passing all three checks are passed to the loading phase.
Dynamic Loading Architecture
Once validated, plugins are instantiated through a dynamic component system that isolates each extension in its own runtime context.
Component Instantiation via QML Loaders
For each valid plugin, the registry creates a Loader element. The loader’s sourceComponent property is bound to the plugin’s entry QML file (for example, Panel.qml for panel-type plugins). This is exposed through the listPlugins property of the registry, which returns a JSON array of loaded plugin metadata sorted alphabetically.
When the loader instantiates, the QML component compiles and executes its Component.onCompleted hook, allowing the plugin to register its UI elements with the shell containers.
Configuration Injection
The registry injects per-plugin settings from ~/.config/omarchy/shell.json directly into each loader via property bindings. This enables runtime configuration of parameters such as position, shortcut, or custom enabled states without modifying the plugin source code. Each plugin receives its configuration subset as context properties before Component.onCompleted runs.
Hot Reload Implementation
Omarchy supports iterative development through a file-watching mechanism that refreshes plugins without restarting the shell session.
File System Watching
The registry utilizes Quickshell’s built-in FileWatcher QML type to monitor every plugin directory. When any file within a watched directory changes—whether the QML source, manifest.json, or auxiliary resources—the watcher emits a fileChanged signal captured by shell/services/PluginRegistry.qml.
Reload Sequence
Upon detecting a change, the registry executes a deterministic unload-reload sequence:
- Unload – Sets
loader.sourceComponent = undefinedto destroy the existing component instance and free associated resources. - Re-validation – Re-reads the
manifest.jsonto capture any metadata updates, renamed entry files, or type changes. - Re-instantiate – Re-assigns
loader.sourceComponentto the refreshed QML file, triggering recompilation and initialization.
Because each plugin is isolated within its own Loader, hot reloading affects only the targeted plugin. Transient UI state (such as open dropdowns or scroll positions) is discarded during reload, which aligns with the "refresh-and-reset" development workflow.
Manual Reload Trigger
Developers can force a full registry refresh by running the omarchy-reload-plugins command located in bin/. This utility signals the shell to re-evaluate all plugin directories immediately, bypassing the file watcher.
Creating a Compatible Plugin
To integrate with the registry, a plugin must follow the directory structure and metadata format expected by the discovery logic.
// shell/plugins/panels/example/manifest.json
{
"name": "example",
"type": "panel",
"entry": "Panel.qml",
"enabled": true
}
// shell/plugins/panels/example/Panel.qml
import QtQuick 2.15
import Quickshell 0.1
Item {
id: root
width: 200; height: 40
Text { text: "Example Panel"; anchors.centerIn: parent }
}
After placing these files in shell/plugins/panels/example/, the registry discovers the plugin on the next shell start. If you edit Panel.qml while the shell is running, the FileWatcher triggers an immediate hot reload, refreshing the panel UI without disturbing other components.
Summary
- Discovery – The registry scans
shell/plugins/for directories containingmanifest.json, validates the entry QML exists, and checks enablement status in~/.config/omarchy/shell.json. - Loading – Valid plugins are loaded via isolated
Loaderelements with configuration injected through property bindings, exposing alistPluginsproperty for shell consumption. - Hot Reload –
FileWatchermonitors plugin directories; on change, the registry unloads (sourceComponent = undefined) and re-instantiates the component to reflect updates immediately. - Manual Control – The
bin/omarchy-reload-pluginscommand forces a full registry refresh outside of the file-watching mechanism.
Frequently Asked Questions
How does the Omarchy plugin registry locate new plugins?
The registry scans the shell/plugins/ directory (and additional paths specified in shell.json) for subdirectories containing a manifest.json file. It validates that the manifest’s entry field points to an existing QML file and that the plugin is enabled in the user configuration file ~/.config/omarchy/shell.json.
What happens to UI state when a plugin hot reloads?
Transient UI state is discarded because the reload sequence sets loader.sourceComponent = undefined, which destroys the old component instance entirely. The new instance starts fresh with default property values and re-executes Component.onCompleted. This behavior is intentional for development workflows where a clean state is preferred.
Can plugins be disabled without removing their files from the system?
Yes. Users can set enabled: false for the specific plugin entry in ~/.config/omarchy/shell.json. The registry tracks disabled plugins in its internal state but excludes them from the listPlugins property, preventing instantiation without deleting the plugin directory from shell/plugins/.
How do I manually trigger a plugin reload if the file watcher fails?
Execute the omarchy-reload-plugins command found in the bin/ directory of the Omarchy repository. This CLI wrapper sends a signal to the running shell instance, forcing shell/services/PluginRegistry.qml to re-scan all plugin directories and reload enabled extensions regardless of file system events.
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 →