# How Hyprland Workspace Rules Work: Parsing, Storage, and Runtime Enforcement

> Understand Hyprland workspace rules. Learn how parsing, storage, and runtime enforcement of rules bind workspaces to monitors and configure attributes for a streamlined workflow.

- Repository: [Hypr Development/Hyprland](https://github.com/hyprwm/Hyprland)
- Tags: internals
- Published: 2026-07-27

---

**Hyprland workspace rules bind workspaces to monitors, set persistence, and configure other attributes by parsing `workspace` lines into `CWorkspaceRule` objects that are stored in `WorkspaceRuleManager` and applied during workspace creation.**

The `hyprwm/Hyprland` source code implements workspace rules through a dedicated pipeline that converts configuration directives into runtime behavior. Understanding how Hyprland workspace rules work requires examining the parser, the rule manager, and the workspace creation logic. The system relies on `WorkspaceRuleManager` to resolve which monitor should host a workspace and whether that workspace should persist across sessions.

## Parsing Configuration into CWorkspaceRule Objects

When Hyprland reads [`hyprland.conf`](https://github.com/hyprwm/Hyprland/blob/main/hyprland.conf), it processes each line beginning with the `workspace` keyword. The parser normalizes the workspace selector and instantiates a `CWorkspaceRule` defined in [`src/config/shared/workspace/WorkspaceRule.hpp`](https://github.com/hyprwm/Hyprland/blob/main/src/config/shared/workspace/WorkspaceRule.hpp).

### The Workspace Keyword Syntax

Each rule starts with the `workspace` directive followed by a selector and key-value pairs. The parser distinguishes between numeric IDs, named workspaces, and special workspaces using the `special:` prefix.

The following syntax patterns are supported according to the Hyprland source:

- `workspace = 1 monitor:DP-1` binds workspace `1` to monitor `DP-1`.
- `workspace = code persistent:true monitor:HDMI-A-1` creates a named workspace `code`, pins it to `HDMI-A-1`, and makes it persistent.
- `workspace = special:something enabled:false` defines a special workspace that starts disabled.

### CWorkspaceRule Data Structure

Every parsed rule populates a `CWorkspaceRule` object with these key fields:

- `m_workspaceString` — the raw selector string from the configuration.
- `m_workspaceId` — the resolved numeric workspace ID.
- `m_monitor` — the target monitor name.
- `m_persistent` — a flag indicating the workspace survives reloads.
- `m_enabled` — a flag controlling whether the workspace starts active.

Special workspaces receive IDs in the range starting at `SPECIAL_WORKSPACE_START`, which the parser handles separately from standard numeric or named workspaces.

## Storing Rules in WorkspaceRuleManager

After parsing, all `CWorkspaceRule` instances live inside the global `Config::workspaceRuleMgr()` singleton implemented in [`src/config/shared/workspace/WorkspaceRuleManager.cpp`](https://github.com/hyprwm/Hyprland/blob/main/src/config/shared/workspace/WorkspaceRuleManager.cpp). This manager provides the central lookup API used by the compositor and plugins.

### Rule Manager API

The manager exposes helper methods for querying rules without scanning the configuration again:

- `getBoundMonitorForWS(const std::string& wsName)` returns the monitor name a workspace should occupy, if a rule exists.
- `getAllWorkspaceRules()` yields the full list for iteration.

Persistent workspace IDs are tracked internally in the manager’s `persistentWorkspaceIDs` vector. This vector guarantees that persistent IDs are not reused and remain stable across configuration reloads.

## Applying Workspace Rules at Runtime

Rule enforcement happens during workspace creation, not during parsing. When a new workspace is spawned, `CWorkspaceStateTracker::createWorkspace(...)` in [`src/state/WorkspaceState.cpp`](https://github.com/hyprwm/Hyprland/blob/main/src/state/WorkspaceState.cpp) queries `WorkspaceRuleManager` to determine placement and properties. The runtime also uses utilities in [`src/state/WorkspaceQueryCore.hpp`](https://github.com/hyprwm/Hyprland/blob/main/src/state/WorkspaceQueryCore.hpp) to locate workspaces by ID, name, or rule during processing.

### Monitor Binding During Creation

Inside `CWorkspaceStateTracker::createWorkspace(...)`, the compositor checks for a matching rule before finalizing the workspace:

```cpp
// Inside CWorkspaceStateTracker::createWorkspace(...)
if (const auto pMonitor = Config::workspaceRuleMgr()->getBoundMonitorForWS(NAME); pMonitor)
    // The workspace is bound to a monitor → assign it

```

If `getBoundMonitorForWS` returns a monitor, the workspace is added to that monitor’s workspace list. The runtime also marks the monitor as seen for that workspace via `m_seenMonitorWorkspaceMap`, ensuring subsequent lookups respect the initial binding.

### Persistent and Special Workspaces

Persistent workspaces rely on the `persistentWorkspaceIDs` vector inside the rule manager. Because these IDs are reserved, the compositor will not destroy or recycle them during normal session cleanup. Special workspaces identified by the `special:` prefix use distinct ID ranges and can carry flags such as `enabled:false` to start in a disabled state.

## Practical Configuration Examples

You can define and query workspace rules from the configuration file, plugins, or external scripts.

### hyprland.conf Syntax

Add the following line to [`hyprland.conf`](https://github.com/hyprwm/Hyprland/blob/main/hyprland.conf) to pin workspace `3` to `HDMI-A-1` and keep it persistent:

```conf

# Pin workspace 3 to HDMI-A-1 and keep it persistent

workspace = 3 monitor:HDMI-A-1 persistent:true

```

### Querying Rules from a Plugin

Plugin authors can access the rule manager directly through the `Config` namespace. The snippet below logs the monitor binding for workspace `3`:

```cpp
auto* ruleMgr = Config::workspaceRuleMgr();
if (auto monitor = ruleMgr->getBoundMonitorForWS("3"); monitor) {
    Hyprland::Log::info("Workspace 3 is bound to monitor {}", *monitor);
}

```

### Creating Rules via hyprctl

You can also inject workspace rules at runtime using a shell command:

```bash
hyprctl --batch "keyword workspace = chat persistent:true monitor:DP-1"

```

## Summary

Hyprland workspace rules bridge configuration text and runtime window management through a three-stage pipeline:

- **Parsing:** The config parser reads `workspace` lines and builds `CWorkspaceRule` objects in [`src/config/shared/workspace/WorkspaceRule.hpp`](https://github.com/hyprwm/Hyprland/blob/main/src/config/shared/workspace/WorkspaceRule.hpp).
- **Storage:** [`WorkspaceRuleManager.cpp`](https://github.com/hyprwm/Hyprland/blob/main/WorkspaceRuleManager.cpp) maintains all rules and provides `getBoundMonitorForWS()` for fast lookup.
- **Application:** [`WorkspaceState.cpp`](https://github.com/hyprwm/Hyprland/blob/main/WorkspaceState.cpp) applies monitor bindings and persistence flags when `CWorkspaceStateTracker::createWorkspace(...)` runs.

## Frequently Asked Questions

### What is the syntax for binding a workspace to a monitor in Hyprland?

Use the `workspace` keyword followed by the workspace selector and `monitor:<name>`. For example, `workspace = 1 monitor:DP-1` tells Hyprland to open workspace `1` on monitor `DP-1`. You can combine this with other flags like `persistent:true` in the same rule.

### How does Hyprland store workspace rules internally?

The compositor stores every parsed rule as a `CWorkspaceRule` object inside `Config::workspaceRuleMgr()`, an instance of the class declared in [`src/config/shared/workspace/WorkspaceRuleManager.hpp`](https://github.com/hyprwm/Hyprland/blob/main/src/config/shared/workspace/WorkspaceRuleManager.hpp). The manager keeps a vector of all rules and a separate `persistentWorkspaceIDs` vector to track which workspaces must survive reloads.

### When does Hyprland apply workspace rules to a monitor?

Rules are applied during workspace creation inside `CWorkspaceStateTracker::createWorkspace(...)` in [`src/state/WorkspaceState.cpp`](https://github.com/hyprwm/Hyprland/blob/main/src/state/WorkspaceState.cpp). The function calls `getBoundMonitorForWS()` to determine whether a rule forces the workspace onto a specific monitor before the workspace becomes visible.

### Can plugins query workspace rules at runtime?

Yes. Plugins can call `Config::workspaceRuleMgr()->getBoundMonitorForWS(const std::string& wsName)` to retrieve a workspace’s bound monitor, or `getAllWorkspaceRules()` to iterate over the entire rule set defined in [`src/config/shared/workspace/WorkspaceRuleManager.cpp`](https://github.com/hyprwm/Hyprland/blob/main/src/config/shared/workspace/WorkspaceRuleManager.cpp).