hyprspaces is a Hyprland plugin that keeps paired workspaces in sync across multiple monitors.
yay -S hyprspaces waybar-hyprspaces-fork-bin
hyprspaces-installyay -S hyprspaces
hyprspaces-install --skip-waybarhyprspaces-waybar-use-fork
hyprspaces-waybar-use-stockHyprland plugins must be rebuilt whenever Hyprland itself is updated. If the plugin fails to load, a desktop notification will appear with instructions.
yay -S hyprspaces
hyprspaces-installhyprspaces-uninstallUseful flags:
--keep-fork: keep~/.local/share/hyprspaces/waybar-fork--skip-waybar: skip stock switchback and shim cleanup--purge-config: remove managed settings/bindings snippets
Each paired workspace index is shown simultaneously across all managed monitors. Switching to pair 3 puts workspace 3 on the primary monitor, workspace 13 on the secondary, workspace 23 on the third, and so on.
Default bindings:
Super + 1..0: switch all monitors to a paired workspace indexSuper + Shift + 1..0: move focused window to the same paired index on the current sideSuper + mouse wheel: cycle paired workspacesSuper + Shift + arrow: move window in the given direction- monitor hotplug: automatically rebalance managed workspaces
Plugin options live under plugin:hyprspaces:
| Option | Type | Default | Description |
|---|---|---|---|
paired_offset |
int | 10 |
Workspace offset per monitor slot |
primary_monitor |
string | "" |
Primary monitor name (auto-detected if empty) |
secondary_monitor |
string | "" |
Secondary monitor name (auto-detected if empty) |
monitors |
string | "" |
Ordered comma-separated monitor names for 3+ monitor support |
keep_focused |
int | 1 |
Restore focus to the original monitor after pair switch |
enable_wrapping |
int | 1 |
Wrap around when cycling past the first or last pair |
follow_moved_window |
int | 0 |
Auto-switch to target pair when using move-window with a pair index |
persistent_workspaces |
int | 0 |
Pre-create all managed workspaces during rebalance |
workspace_names |
string | "" |
Pair index names in 1:Web,2:Dev,3:Chat format |
For 2 monitors, primary_monitor and secondary_monitor are sufficient. If both are omitted, the plugin auto-selects the first two active monitors.
For 3+ monitors, set monitors to an ordered comma-separated list of monitor names:
plugin {
hyprspaces {
monitors = DP-1,DP-2,HDMI-A-1
}
}
Monitor slot 0 gets workspaces 1..offset, slot 1 gets offset+1..offset*2, slot 2 gets offset*2+1..offset*3, and so on. When monitors is set, primary_monitor and secondary_monitor are ignored.
| Dispatcher | Arguments | Description |
|---|---|---|
hyprspaces:switch |
<N> or previous |
Switch all monitors to pair N, or return to the previous pair |
hyprspaces:cycle |
next or prev |
Cycle to the next or previous pair |
hyprspaces:move-window |
<N> or l|r|u|d |
Move focused window to pair N on the current side, or directionally |
hyprspaces:move-window-follow |
<N> |
Move focused window to pair N and switch to that pair |
hyprspaces:rebalance |
Force-rebalance all managed workspaces onto correct monitors | |
hyprspaces:swap-sides |
Swap windows between current and next monitor for the active pair | |
hyprspaces:move-to-side |
Move focused window to the next monitor within the active pair | |
hyprspaces:focus-side |
Cycle focus to the next monitor | |
hyprspaces:toggle-special |
[name] |
Toggle a special workspace on all monitors simultaneously |
hyprspaces:bring-window |
<N> |
Pull the last window from pair N on the same side to the current workspace |
Examples:
hyprctl dispatch hyprspaces:switch 3
hyprctl dispatch hyprspaces:switch previous
hyprctl dispatch hyprspaces:cycle next
hyprctl dispatch hyprspaces:move-window 4
hyprctl dispatch hyprspaces:move-window r
hyprctl dispatch hyprspaces:swap-sides
hyprctl dispatch hyprspaces:toggle-special
hyprctl dispatch hyprspaces:bring-window 2
hyprctl dispatch hyprspaces:rebalanceAll dispatchers emit events on Hyprland's socket2 under the hyprspaces topic:
| Event | Format |
|---|---|
| Pair switch | hyprspaces>>switch,<N> |
| Cycle | (emitted as a switch event) |
| Move window | hyprspaces>>move-window,<N or direction> |
| Move window follow | hyprspaces>>move-window-follow,<N> |
| Rebalance | hyprspaces>>rebalance |
| Swap sides | hyprspaces>>swap,<N> |
| Move to side | hyprspaces>>move-to-side |
| Focus side | hyprspaces>>focus-side |
| Toggle special | hyprspaces>>toggle-special,<name> |
| Bring window | hyprspaces>>bring-window,<N> |
| Topology change | hyprspaces>>rebalance (also emitted on monitor hotplug and config reload) |
Listen with:
socat -U - UNIX-CONNECT:$XDG_RUNTIME_DIR/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock | grep hyprspacesExample: react to pair switches in a script:
socat -U - UNIX-CONNECT:$XDG_RUNTIME_DIR/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock | while IFS= read -r line; do
case "$line" in
hyprspaces\>\>switch,*) echo "Switched to pair ${line##*,}" ;;
hyprspaces\>\>rebalance) echo "Workspaces rebalanced" ;;
esac
doneA reference waybar config is available at config/waybar-example.jsonc.
Query plugin state:
hyprctl hyprspaces # plain text
hyprctl -j hyprspaces # JSONJSON output includes: version, activePair, previousPair, pairedOffset, primaryMonitor, secondaryMonitor, monitorCount, monitors (each with name and activeWorkspace), workspaceNames, keepFocused, enableWrapping, followMovedWindow, persistentWorkspaces.
- fork:
https://github.com/jtaw5649/Waybar(hyprspaces-paired-workspaces) - upstream base module:
https://github.com/Alexays/Waybar/blob/master/src/modules/hyprland/workspaces.cpp - added behavior:
active-per-monitor,hyprspaces-paired-offset, paired-offset-aware persistent workspace creation,hyprspaces-special-overlay
Special workspace indicator flow (fork mode):
- keep
show-special: falseto avoid a separate scratchpad button in the workspace row - set
hyprspaces-special-overlay: trueto mark the active numbered workspace when a special workspace is visible on that monitor - define
format-icons.special-activeto choose the icon shown during special overlay state - style with
#workspaces button.special-active/#workspaces button.special-active.active hyprspaces-waybar-use-forkwritesshow-special: falseandhyprspaces-special-overlay: trueautomatically
Credit:
- the
hyprland/workspacesimplementation in this fork is based on Waybar's upstream module - all credit for the original module architecture and baseline behavior goes to Waybar maintainers and contributors
- Waybar is MIT-licensed:
https://github.com/Alexays/Waybar/blob/master/LICENSE
The switch commands manage:
~/.config/hyprspaces/waybar/current-binary- managed
waybarshim(s) in user-writable PATH entries - backups in
~/.local/state/hyprspaces/waybar/
Build:
cmake -S . -B build
cmake --build buildor:
make allTests:
ctest --test-dir build --output-on-failure