Multi-head workspace setup in Omarchy
Hello, I compiled some defaults for a better multi-head setup in Omarchy. I understand that @dhh and a lot of folks enjoy the single head setup.
I do like it too, but sometimes, I want to plug my notebook to my big
monitor, or to have a portrait monitor for writing docs or reading.
Whatever the case might be, the Omarchy defaults for a multi-head are
not very good.
Workspaces get messy as which one is in which monitor and no keybinds to
(for example) move a window to a empty monitor workspace. So, I wrote a
small guide to what I think is a better setup in Omarchy for a
multi-head workspace setup.
Goal
- Better manage a multi-head setup.
- Avoid the inconsistency of which monitor has workspace X.
- Each Monitor to has its own set of workspaces.
- Switch focus between monitors.
- Move windows direct to another monitor.
Updated Keybidings
SUPER + CTRL to handle monitor interaction.
To focus desired monitor:
SUPER + CTRL + < monitor index >
SUPER + CTRL +
LEFT/RIGHT To move a window to the a
monitor:
(even if the current workspace is empty)
SUPER + CTRL + SHIFT + <
monitor index >
SUPER + CTRL + SHIFT +
LEFT/RIGHT
Plugin
Dependencies
sudo pacman -S cpio
Update headers
hyprpm update
Install
hyprpm add https://github.com/Duckonaut/split-monitor-workspaces
hyprpm enable split-monitor-workspaces
hyprpm reload -nConfiguration
Add in ~/.config/hypr/hyprland.conf:
# Plugins Configurtion
source = ~/.config/hypr/plugin-split-monitor-workspace.conf
Create ~/.config/hypr/plugin-split-monitor-workspace.conf:
# Auto-load plugins on startup (recommended by Hyprland)
exec-once = hyprpm reload -n
# --- split-monitor-workspaces ---
plugin {
split-monitor-workspaces {
count = 10
keep_focused = 1
enable_persistent_workspaces = 0
enable_notifications = 0
}
}
# unbind Omarchy defaults without touching them
unbind = SUPER, code:10
unbind = SUPER, code:11
unbind = SUPER, code:12
unbind = SUPER, code:13
unbind = SUPER, code:14
unbind = SUPER, code:15
unbind = SUPER, code:16
unbind = SUPER, code:17
unbind = SUPER, code:18
unbind = SUPER, code:19
unbind = SUPER SHIFT, code:10
unbind = SUPER SHIFT, code:11
unbind = SUPER SHIFT, code:12
unbind = SUPER SHIFT, code:13
unbind = SUPER SHIFT, code:14
unbind = SUPER SHIFT, code:15
unbind = SUPER SHIFT, code:16
unbind = SUPER SHIFT, code:17
unbind = SUPER SHIFT, code:18
unbind = SUPER SHIFT, code:19
##### WORKSPACES (per monitor, via plugin) #####
# SUPER + [1..0] = switch workspace on current monitor
bindd = SUPER, code:10, Switch WS 1, split-workspace, 1
bindd = SUPER, code:11, Switch WS 2, split-workspace, 2
bindd = SUPER, code:12, Switch WS 3, split-workspace, 3
bindd = SUPER, code:13, Switch WS 4, split-workspace, 4
bindd = SUPER, code:14, Switch WS 5, split-workspace, 5
bindd = SUPER, code:15, Switch WS 6, split-workspace, 6
bindd = SUPER, code:16, Switch WS 7, split-workspace, 7
bindd = SUPER, code:17, Switch WS 8, split-workspace, 8
bindd = SUPER, code:18, Switch WS 9, split-workspace, 9
bindd = SUPER, code:19, Switch WS 0, split-workspace, 10
# SUPER + SHIFT + [1..0] = move window to workspace (keep focus here)
bindd = SUPER SHIFT, code:10, Move win to WS 1, split-movetoworkspace, 1
bindd = SUPER SHIFT, code:11, Move win to WS 2, split-movetoworkspace, 2
bindd = SUPER SHIFT, code:12, Move win to WS 3, split-movetoworkspace, 3
bindd = SUPER SHIFT, code:13, Move win to WS 4, split-movetoworkspace, 4
bindd = SUPER SHIFT, code:14, Move win to WS 5, split-movetoworkspace, 5
bindd = SUPER SHIFT, code:15, Move win to WS 6, split-movetoworkspace, 6
bindd = SUPER SHIFT, code:16, Move win to WS 7, split-movetoworkspace, 7
bindd = SUPER SHIFT, code:17, Move win to WS 8, split-movetoworkspace, 8
bindd = SUPER SHIFT, code:18, Move win to WS 9, split-movetoworkspace, 9
bindd = SUPER SHIFT, code:19, Move win to WS 0, split-movetoworkspace, 10
# Cycle workspaces on current monitor
bindd = SUPER, TAB, Cycle WS next, split-cycleworkspaces, next
bindd = SUPER SHIFT, TAB, Cycle WS prev, split-cycleworkspaces, prev
##### ABSOLUTE MONITOR FOCUS (SUPER + CTRL + <position>)
bindd = SUPER CTRL, code:10, Focus mon 0, focusmonitor, 0
bindd = SUPER CTRL, code:11, Focus mon 1, focusmonitor, 1
bindd = SUPER CTRL, code:12, Focus mon 2, focusmonitor, 2
bindd = SUPER CTRL, code:13, Focus mon 3, focusmonitor, 3
bindd = SUPER CTRL, code:14, Focus mon 4, focusmonitor, 4
##### MOVE WINDOW TO MONITOR (by ID, keep focus here)
# SUPER+CTRL+SHIFT+[1..3]
bindd = SUPER CTRL SHIFT, code:10, Send win to mon 0, movewindow, mon:0
bindd = SUPER CTRL SHIFT, code:11, Send win to mon 1, movewindow, mon:1
bindd = SUPER CTRL SHIFT, code:12, Send win to mon 2, movewindow, mon:2
bindd = SUPER CTRL SHIFT, code:13, Send win to mon 3, movewindow, mon:3
bindd = SUPER CTRL SHIFT, code:14, Send win to mon 4, movewindow, mon:4
##### ARROW NAV (relative; monitor-agnostic) #####
# SUPER + LEFT/RIGHT = focus monitor left/right
bindd = SUPER CTRL, LEFT, Focus monitor left, focusmonitor, l
bindd = SUPER CTRL, RIGHT, Focus monitor right, focusmonitor, r
# SUPER + SHIFT + LEFT/RIGHT = send window to prev/next monitor
bindd = SUPER CTRL SHIFT, LEFT, Send win to left mon, movewindow, mon:l
bindd = SUPER CTRL SHIFT, RIGHT, Send win to right mon, movewindow, mon:r
Waybar
Update Waybar to better reflect it in ~/.config/waybar/style.css:
...
"hyprland/workspaces": {
"all-outputs": false, // per-monitor list
"sort-by-number": true,
"disable-scroll": true, // avoid scroll jumps
"format": "{icon}",
// Dots: filled = active here, ring = active elsewhere, hollow = has windows, dim = empty
"format-icons": {
"urgent": "",
"active": "", // current monitor's focused WS
"visible": "", // focused WS on another monitor
"default": "", // has windows
"empty": "·" // created by split-monitor-workspaces but empty
}
},
...
jpSkipper Sep 25, 2025
-
Thank you very much. One small thing, i was a little bit confused by:
…Waybar
Update Waybar to better reflect it in ~/.config/waybar/style.css:
…
I guess you mean the ~/.config/waybar/config.jsonc instead
of the css file.
—
opajanvv Sep 26, 2025
-
A few comments:
- When installing the dependencies, I had to install:
cpio,cmake, andmeson. - I recommend creating the new config for split-monitor-workspace first, and then adding the source command to hyprland.conf. That avoids a nasty error message that stays on your screen until you save hyprland.conf again.
- And @jpSkipper is right about editing the config instead of the stylesheet.
Anyway, thanks a lot for this walkthrough! Really helpful. Without it, I would have ditched Omarchy because of the lack of good workspace management in a dual-screen setup. I changed the setup to my own preferences:
- Workspaces 1..4 on the first screen, workspaces 5..8 on the other
- SUPER + jumps to the proper workspace, no matter on which monitor it is
- SUPER SHIFT + moves the current window to the new workspace, even when it is on the other monitor
- Proper workspace cycling when using SUPER TAB
- Highlight the current workspace in waybar
- Waybar shows all workspaces (not only the ones having a window)
It took me a few hours to get this working properly, so I’ll share my source files. `plugin-split-monitor-workspace.conf
# Auto-load plugins on startup (recommended by Hyprland)
exec-once = hyprpm reload -n
# --- split-monitor-workspaces ---
plugin {
split-monitor-workspaces {
count = 4
keep_focused = 1
enable_persistent_workspaces = 1
enable_notifications = 1
enable_wrapping = 1
}
}
# unbind Omarchy defaults without touching them
unbind = SUPER, code:10
unbind = SUPER, code:11
unbind = SUPER, code:12
unbind = SUPER, code:13
unbind = SUPER, code:14
unbind = SUPER, code:15
unbind = SUPER, code:16
unbind = SUPER, code:17
unbind = SUPER, code:18
unbind = SUPER, code:19
unbind = SUPER SHIFT, code:10
unbind = SUPER SHIFT, code:11
unbind = SUPER SHIFT, code:12
unbind = SUPER SHIFT, code:13
unbind = SUPER SHIFT, code:14
unbind = SUPER SHIFT, code:15
unbind = SUPER SHIFT, code:16
unbind = SUPER SHIFT, code:17
unbind = SUPER SHIFT, code:18
unbind = SUPER SHIFT, code:19
##### WORKSPACES (per monitor, via plugin) #####
# WS 1–4 on monitor 0
bindd = SUPER, code:10, WS1, exec, hyprctl dispatch focusmonitor 0; hyprctl dispatch split-workspace 1
bindd = SUPER, code:11, WS2, exec, hyprctl dispatch focusmonitor 0; hyprctl dispatch split-workspace 2
bindd = SUPER, code:12, WS3, exec, hyprctl dispatch focusmonitor 0; hyprctl dispatch split-workspace 3
bindd = SUPER, code:13, WS4, exec, hyprctl dispatch focusmonitor 0; hyprctl dispatch split-workspace 4
# WS 5–8 on monitor 1
bindd = SUPER, code:14, WS5, exec, hyprctl dispatch focusmonitor 1; hyprctl dispatch split-workspace 1
bindd = SUPER, code:15, WS6, exec, hyprctl dispatch focusmonitor 1; hyprctl dispatch split-workspace 2
bindd = SUPER, code:16, WS7, exec, hyprctl dispatch focusmonitor 1; hyprctl dispatch split-workspace 3
bindd = SUPER, code:17, WS8, exec, hyprctl dispatch focusmonitor 1; hyprctl dispatch split-workspace 4
# SUPER + SHIFT + [1..8] = move window to workspace (and move focus there)
# WS 1–4 on monitor 0
bindd = SUPER SHIFT, code:10, Move win to WS 1, exec, hyprctl dispatch movewindow mon:0; hyprctl dispatch focusmonitor 0; hyprctl dispatch split-movetoworkspace 1
bindd = SUPER SHIFT, code:11, Move win to WS 2, exec, hyprctl dispatch movewindow mon:0; hyprctl dispatch focusmonitor 0; hyprctl dispatch split-movetoworkspace 2
bindd = SUPER SHIFT, code:12, Move win to WS 3, exec, hyprctl dispatch movewindow mon:0; hyprctl dispatch focusmonitor 0; hyprctl dispatch split-movetoworkspace 3
bindd = SUPER SHIFT, code:13, Move win to WS 4, exec, hyprctl dispatch movewindow mon:0; hyprctl dispatch focusmonitor 0; hyprctl dispatch split-movetoworkspace 4
# WS 5–8 on monitor 1 → remap to local 1–4
bindd = SUPER SHIFT, code:14, Move win to WS 5, exec, hyprctl dispatch movewindow mon:1; hyprctl dispatch focusmonitor 1; hyprctl dispatch split-movetoworkspace 1
bindd = SUPER SHIFT, code:15, Move win to WS 6, exec, hyprctl dispatch movewindow mon:1; hyprctl dispatch focusmonitor 1; hyprctl dispatch split-movetoworkspace 2
bindd = SUPER SHIFT, code:16, Move win to WS 7, exec, hyprctl dispatch movewindow mon:1; hyprctl dispatch focusmonitor 1; hyprctl dispatch split-movetoworkspace 3
bindd = SUPER SHIFT, code:17, Move win to WS 8, exec, hyprctl dispatch movewindow mon:1; hyprctl dispatch focusmonitor 1; hyprctl dispatch split-movetoworkspace 4
# Cycle workspaces on current monitor
bindd = SUPER, TAB, Cycle WS next, split-cycleworkspaces, next
bindd = SUPER SHIFT, TAB, Cycle WS prev, split-cycleworkspaces, prev
# SUPER + SHIFT + LEFT/RIGHT = send window to prev/next monitor
bindd = SUPER CTRL SHIFT, LEFT, Send win to left mon, movewindow, mon:l
bindd = SUPER CTRL SHIFT, RIGHT, Send win to right mon, movewindow, mon:r
# Fix workspace cycling
unbind = SUPER, TAB
unbind = SUPER SHIFT, TAB
bind = SUPER, TAB, split-workspace, +1
bind = SUPER SHIFT, TAB, split-workspace, -1
waybar/config.jsonc
"hyprland/workspaces": {
"all-outputs": false,
"sort-by-number": true,
"disable-scroll": true,
"on-click": "activate",
"format": "{icon}",
},
waybar/style.css
#workspaces button {
all: initial;
padding: 0 6px;
margin: 0 1.5px;
min-width: 9px;
opacity: 0.5;
}
#workspaces button.visible,
#workspaces button.active {
font-weight: bold;
opacity: 1;
}
7
brombirium Oct 13, 2025
opajanvv Oct 13, 2025
-
Thanks.
I’m gathering all my experiences with Omarchy in a blog, mainly for my
own reference, so I can see why I made certain decisions. But of course,
others can also use it to get ideas for their own configuration. It’s
not complete yet, so feel free to check back from time to time to see if
I’ve published a new blog post.
See https://opa.janvv.nl/tag:omarchy.
—
NawwafHusain Oct 24, 2025
-
Thank you guys, this has helped a lot! I want to extend the functionality a bit, but unsure where to even begin/if it possible. Currently I am running @opajanvv ’s set up, with wp 1-4 on my laptop and 5-8 on my monitor. What I want to achieve is, when no monitors are connected to the laptop, for the laptop to have 8 workspaces. When connected, the workspaces “move” to the new monitor. The reason I’m emphasizing move not create new ones, is that in my current set up, if i have a window in wp 5 and disconnect my HDMI, that window is “lost”, weirdly enough, when I reconnect, the window is still there in the same state.
opajanvv Oct 24, 2025
-
Hey @NawwafHusain, that’s exactly what I was looking for. I already have a bash script that switches between three modes: laptop only, dual screen, and laptop mirrored (for presentations). It also requires a few other config files. I’m planning to document this in a blog article today or tomorrow. I still want to add keyboard shortcuts for it. Stay tuned.
opajanvv Oct 24, 2025
-
@NawwafHusain new blog post is published. Hope it works for you. See the link in the previous post.
patroza Oct 26, 2025
-
im trying this with a virtual monitor connected to through vnc, but the whole separate workspaces per desktop is not really working as expected for me on latest omarchy.
opajanvv Oct 27, 2025
-
I don’t have experience with virtual monitors.
What’s the output of hyprctl monitors all?
—
patroza Oct 27, 2025
-
here it is, however, actually since switching my studio display from thunderbolt to split displayport+usb, it seems like it’s working well (also revealing the audio input/output and camera :)), I now have 5-8 on display 2. and 1-4 on display 1.
Monitor DP-1 (ID 0):
5120x2880@60.00000 at 0x0
description: Apple Computer Inc StudioDisplay 0x44030964
make: Apple Computer Inc
model: StudioDisplay
physical size (mm): 600x330
serial: 0x44030964
active workspace: 4 (4)
special workspace: 0 ()
reserved: 0 26 0 0
scale: 2.00
transform: 0
focused: yes
dpmsStatus: 1
vrr: false
solitary: 0
solitaryBlockedBy: windowed mode,missing candidate
activelyTearing: false
tearingBlockedBy: next frame is not torn,user settings,missing candidate
directScanoutTo: 0
directScanoutBlockedBy: user settings,screen record/screenshot,software renders/cursors,missing candidate
disabled: false
currentFormat: XRGB8888
mirrorOf: none
availableModes: 5120x2880@60.00Hz 3840x2160@60.00Hz 2560x2880@60.00Hz 2560x1440@60.00Hz 1920x1080@60.00Hz
Monitor HEADLESS-66 (ID 1):
3456x2234@60.00000 at 2560x450
description:
make:
model:
physical size (mm): 0x0
serial:
active workspace: 5 (5)
special workspace: 0 ()
reserved: 0 26 0 0
scale: 2.00
transform: 0
focused: no
dpmsStatus: 1
vrr: false
solitary: 0
solitaryBlockedBy: windowed mode,missing candidate
activelyTearing: false
tearingBlockedBy: next frame is not torn,user settings,not supported by monitor,missing candidate
directScanoutTo: 0
directScanoutBlockedBy: user settings,screen record/screenshot,software renders/cursors,missing candidate
disabled: false
currentFormat: XRGB8888
mirrorOf: none
availableModes: 1920x1080@0.06Hz
opajanvv Oct 27, 2025
-
Super. Glad you got it working.
Contributors:
© 2026 rcanzlovar.com | About | Contact | Privacy Policy |
![]()