# How Quickshell Desktop Integrates with Hyprland: A Deep Dive Into Omacom/Omarchy

> Discover how Quickshell integrates with Hyprland using its QML module. Learn how it exposes compositor state and forwards events for seamless desktop experience.

- Repository: [Omacom/omarchy](https://github.com/omacom/omarchy)
- Tags: deep-dive
- Published: 2026-09-06

---

**Quickshell integrates with Hyprland through the `Quickshell.Hyprland` QML module, which exposes compositor state as reactive properties and forwards events via `hyprctl` and the compositor's socket.**

The Omarchy project (hosted at `omacom/omarchy`) uses Quickshell as its core UI engine—a generic QML-based desktop shell that adapts to any Wayland compositor. This article explains how the Hyprland-specific integration works, covering state fetching, event handling, and real-world code patterns from the repository.

---

## Importing the Hyprland Module in Quickshell

UI components that need compositor data import `Quickshell.Hyprland`:

```qml
import Quickshell.Hyprland

```

This exposes a **JavaScript singleton object named `Hyprland`** with properties that mirror Hyprland's runtime state:

- `Hyprland.workspaces` — available workspaces with IDs and metadata
- `Hyprland.focusedMonitor` — currently focused monitor
- `Hyprland.focusedWorkspace` — active workspace
- Configuration values like `general:gaps_out` and `decoration:rounding`

These properties update automatically when the compositor state changes, enabling reactive UI components without manual polling in application code.

---

## Fetching Compositor State via hyprctl

The `Quickshell.Hyprland` module communicates with Hyprland through two mechanisms: the `hyprctl` command-line interface and Hyprland's dedicated socket.

For values that Hyprland does not push (such as `general:gaps_out` and `decoration:rounding`), the module **periodically polls** `hyprctl get ...` and caches results in the `Hyprland` singleton. The cache refreshes when underlying configuration files change.

This polling mechanism enables automatic UI synchronization. In `shell/Commons/Style.qml`, the desktop's visual properties derive directly from Hyprland configuration:

```qml
// shell/Commons/Style.qml
property real cornerRadius: Hyprland.decoration.rounding
property real gapsOut: Hyprland.general.gaps_out

```

When a user modifies their Hyprland config and reloads, Quickshell's bar, popovers, and widgets automatically adopt the new corner radius and gap sizes.

---

## Reacting to Hyprland Events

Hyprland emits raw events for workspace switches, monitor changes, window focus transitions, and more. The `Quickshell.Hyprland` module registers a listener—`HyprlandFocusGrab`—that receives these events and updates singleton properties.

Downstream services consume these events for compositor-aware behavior. For example, in `shell/plugins/services/idle/Service.qml`, the idle service uses Hyprland events to determine when to trigger screen dimming or locking. Similarly, `shell/plugins/notifications/Service.qml` listens for focus changes to decide whether to show on-screen displays or refocus windows.

---

## Bar-Level Hyprland Integration

The top bar (`shell/plugins/bar/Bar.qml`) imports the Hyprland module to read `Hyprland.focusedMonitor` and locate workspaces. Widgets access this data directly.

The **Workspaces pill** (`shell/plugins/bar/widgets/Workspaces.qml`) demonstrates the full pattern: reading workspace state, computing IDs, and dispatching commands:

```qml
// shell/plugins/bar/widgets/Workspaces.qml
import Quickshell.Hyprland
import qs.Ui

BarWidget {
    id: ws
    moduleName: "omarchy.workspaces"

    function workspaceIds() {
        var ids = [1,2,3,4,5];
        var values = Hyprland.workspaces.values;
        for (var i = 0; i < values.length; i++) {
            var id = values[i].id;
            if (id > 0 && ids.indexOf(id) === -1) ids.push(id);
        }
        ids.sort(function (a,b) { return a-b });
        return ids;
    }

    function focusWorkspace(id) {
        // Canonical command interface: hyprctl dispatch
        root.bar.run(
            "hyprctl dispatch " +
            Util.shellQuote("hl.dsp.focus({ workspace = \"" + id + "\" })")
        );
    }

    GridLayout {
        columns: ws.workspaceIds().length
        Repeater {
            model: ws.workspaceIds()
            WidgetButton {
                readonly property var wsInfo: 
                    Hyprland.workspaces.values.find(w => w.id === modelData)
                readonly property bool focused: 
                    Hyprland.focusedWorkspace?.id === modelData
                text: focused ? "\uDB85\uDCFB" : String(modelData)
                onPressed: ws.focusWorkspace(modelData)
            }
        }
    }
}

```

The button text switches to a special icon when `focused` evaluates true, and clicking dispatches a `hyprctl` command to switch workspaces.

---

## Monitor-Aware Layout

Components that need per-monitor behavior react to `Hyprland.focusedMonitor` changes:

```qml
import Quickshell.Hyprland

Bar {
    onFocusedMonitorChanged: {
        console.log("New monitor:", Hyprland.focusedMonitor.name);
        // Re-layout widgets based on monitor geometry
    }
}

```

This property updates automatically whenever the user moves focus between displays, enabling dynamic widget positioning without manual geometry calculations.

---

## Session Lock Integration

The lock service (`shell/plugins/lock/Service.qml`) creates an `ext-session-lock` client tied to the Hyprland session lifecycle. The `shell/shell.qml` entry point contains comments explaining this interaction: when the lock releases, the client destroys itself, allowing Hyprland to clean up the session lock properly.

This integration ensures that Omarchy's lock screen follows the **ext-session-lock-v1 protocol** correctly, matching Hyprland's expectations for secure screen locking.

---

## Visual Styling from Hyprland Configuration

The desktop maintains visual consistency with the compositor through three key mappings in `shell/Commons/Style.qml`:

- `Style.cornerRadius` → `Hyprland.decoration:rounding`
- `Style.gapsOut` → `Hyprland.general:gaps_out`
- Bar colors → `shell/Commons/Color.qml` (Hyprland-aware palette)

This approach eliminates configuration duplication—users set values once in Hyprland, and Quickshell renders matching UI elements.

---

## Key Files in the Integration

| File | Purpose |
|------|---------|
| `shell/plugins/bar/Bar.qml` | Core bar; reads `Hyprland.focusedMonitor` |
| `shell/plugins/bar/widgets/Workspaces.qml` | Workspace widget; queries `Hyprland.workspaces` and dispatches `hyprctl` |
| `shell/plugins/services/idle/Service.qml` | Registers `Hyprland` event listener for idle management |
| `shell/plugins/notifications/Service.qml` | Window focus and decoration integration |
| `shell/plugins/lock/Service.qml` | `ext-session-lock` client lifecycle |
| `shell/Commons/Style.qml` | Mirrors Hyprland gaps and rounding |
| `shell/Ui/PopupCard.qml` | Uses `HyprlandFocusGrab` for click-outside dismissal |
| `shell/shell.qml` | Entry point with lock/session documentation |

---

## Summary

- **Module import**: `Quickshell.Hyprland` exposes compositor state as reactive QML properties
- **State fetching**: `hyprctl` polling for config values, socket events for live updates
- **Event handling**: `HyprlandFocusGrab` forwards Hyprland events to QML signals
- **Command dispatch**: Widgets call `hyprctl dispatch` for workspace changes and window management
- **Visual sync**: `Style.qml` automatically applies Hyprland gap and rounding settings
- **Session lock**: Proper `ext-session-lock` protocol implementation in `shell/plugins/lock/Service.qml`

---

## Frequently Asked Questions

### What protocol does Quickshell use to communicate with Hyprland?

Quickshell uses Hyprland's native `hyprctl` command-line interface for state queries and the compositor's Unix socket for event streaming. The `Quickshell.Hyprland` module abstracts these into QML properties and signals.

### Can Quickshell work with compositors other than Hyprland?

Yes. Quickshell is designed as a generic Wayland desktop shell. The Hyprland integration lives in the optional `Quickshell.Hyprland` module; other compositors would use their own integration modules or the base Wayland functionality.

### How does the bar know when to update workspace indicators?

The `Hyprland.workspaces` property is reactive. When Hyprland emits a workspace change event, the module updates this property, triggering `onChanged` handlers in QML bindings. The Workspaces widget re-evaluates `workspaceIds()` and the Repeater updates automatically.

### Where are Hyprland's gap and rounding values cached?

The `Quickshell.Hyprland` module caches polled configuration values internally. `shell/Commons/Style.qml` then exposes these as `cornerRadius` and `gapsOut` properties that the rest of the UI consumes.