After years of rock-solid i3 on X11 (and previously AwesomeWM), I decided to tackle moving from i3 to Hyprland. My requirements were pretty basic: keep my exact workflow intact, maintain my vi-style navigation muscle memory, and get proper fractional scaling for my 4K display.
Why Switch Now?
A few things pushed me toward making the jump:
- 4K fractional scaling - X11’s fractional scaling has always been janky, apps looked blurry or tiny
- Picom compositing issues - I was having weird glitches with picom on i3 and wanted to test how Wayland handled compositing
- Future-proofing - Wayland is the future, not sure if X11 development is still active, figured I should bite the bullet
My Non-Negotiables
I’ve spent years using my i3 setup and any migration had to preserve:
- My vi-style navigation - j/k/l/; for movement because I’m fast with these keys
- Password management workflow - rofi-pass integration I rely on daily + with pass
- Clipboard behavior - primary and clipboard selections need to stay in sync, (middle mouse + ctrl+shift+v)
Configuration Translation: i3 → Hyprland
Preserving Navigation
I prefer a vi-style navigation for my WM and CLI. Instead of hjkl, I use jkl; (shifted one key right) which lets me keep my right hand in perfect typing position. This muscle memory is sacred.
# My custom movement mapping
set $left j
set $down k
set $up l
set $right semicolon
# i3 bindings I've been using for years
bindsym $mod+$left focus left
bindsym $mod+$down focus down
bindsym $mod+$up focus up
bindsym $mod+$right focus right
# Hyprland with hy3 (different syntax, same muscle memory)
bind = $mainMod, j, hy3:movefocus, l # Focus left
bind = $mainMod, k, hy3:movefocus, d # Focus down
bind = $mainMod, l, hy3:movefocus, u # Focus up
bind = $mainMod, semicolon, hy3:movefocus, r # Focus right
# Window movement with hy3 (note: mod+shift+l is bound to hyprlock)
bind = $mainMod SHIFT, j, hy3:movewindow, l # Move window left
bind = $mainMod SHIFT, k, hy3:movewindow, d # Move window down
bind = $mainMod SHIFT, semicolon, hy3:movewindow, r # Move window right
# For moving windows up, use arrow keys:
bind = $mainMod SHIFT, up, hy3:movewindow, u # Move window up
# System controls
bind = $mainMod SHIFT, l, exec, pidof hyprlock || hyprlock # Lock screen
The important thing here isn’t the specific keys - it’s that I can navigate between windows without thinking. Window management shouldn’t interrupt flow state.
Back-and-Forth Workspace Switching
One behavior I heavily relied on in i3, and carried over from Awesome, was mod+Esc
to toggle between the current and previous workspace. Turns out Hyprland has this built-in, much cleaner than my initial script approach:
# Enable workspace cycling
binds {
allow_workspace_cycles = true
}
# Bind to previous workspace
bind = $mainMod, Escape, workspace, previous
This preserves the exact behavior I had in i3 - press mod+Esc
to quickly toggle between two workspaces.
Terminal Switch: Wezterm → Ghostty
I’ve been using wezterm for years, but decided to trial ghostty during this migration. The performance claims and simplified config are appealing, plus I’ve been using Zellij-based dev environment more lately.
# Updated terminal binding
bind = $mainMod, Return, exec, ghostty
My thoughts on this switch are still pending - probably material for a future article once I’ve used it longer.
Layout Management: hy3 Plugin for True i3-like Behavior
Initially, I tried to replicate i3’s container model using Hyprland’s built-in group system, but it felt clunky. Then I discovered hy3 - a plugin that brings true i3-like layout management to Hyprland.
# Enable hy3 plugin
plugins {
plugin = hy3
}
general {
layout = hy3 # Use hy3 as the default layout
}
# Load the plugin on startup
exec-once = hyprpm reload -n
# The magic binding - just like i3!
bind = $mainMod, s, hy3:changegroup, toggletab # Toggle between tabbed/tiled
With hy3, mod+s
works exactly like i3’s layout toggle. This was a game-changer - suddenly Hyprland felt like home again. The plugin handles containers, tabbing, and splitting just like i3 did.
Component Replacements
Status Bar: i3bar + i3status-rs → Waybar
My i3 setup used i3status-rs with a custom TOML configuration. Waybar required a JSON config and CSS styling:
// ~/.config/waybar/config
{
"layer": "top",
"position": "bottom",
"height": 30,
"modules-right": [
"tray",
"network",
"cpu",
"disk",
"memory",
"pulseaudio",
"clock",
"custom/dunst"
],
"network": {
"interface": "eno1",
"format": " {ifname} {ipaddr}",
"interval": 30
}
// ... more modules
}
The CSS maintained my Dracula theme:
/* ~/.config/waybar/style.css */
window#waybar {
background-color: #111111;
color: #dddddd;
}
#workspaces button.focused {
background-color: #0088cc;
color: #ffffff;
}
Keeping My Password Workflow: rofi → wofi
I rely heavily on rofi-pass for password management. The transition to wofi needed to preserve this workflow completely:
# i3 launcher with icons
bindsym $mod+d exec "rofi -show drun -lines 30 -show-icons"
# Hyprland wofi
bind = $mainMod, d, exec, wofi --show drun --lines 30
# wofi-pass (password manager integration)
bind = $mainMod, T, exec, wofi-pass --copy
More importantly, wofi-pass works perfectly as a rofi-pass replacement. My password workflow stayed identical - same keybindings, same muscle memory.
Cheatsheet Integration
One nice addition I made was a comprehensive cheatsheet system accessible via mod+shift+tab
. This displays all my keybindings in a floating terminal window:
# Cheatsheet binding
bind = $mainMod SHIFT, Tab, exec, ~/.config/hypr/scripts/show-cheatsheet.sh
# Window rules for the cheatsheet
windowrule = float, class:^(HyprCheatsheet)$
windowrule = center, class:^(HyprCheatsheet)$
The cheatsheet covers all categories - window management, workspaces, applications, clipboard, and more. It’s incredibly helpful when switching between different systems or when I forget a less-used binding.
Improving Copy/Paste
Copy/paste management on Linux is notoriously annoying. I had two main issues to solve: clipboard vs primary selection fragmentation, and getting mod+c
/mod+v
to work universally.
First, syncing clipboard and primary selection:
# Sync primary selection with clipboard
exec-once = wl-paste -p -t text --watch wl-copy
This ensures that when I select text, it automatically goes to the clipboard too. No more clipboard/primary selection confusion.
Second, making mod+c/v work like every other OS:
# Use sendshortcut to make mod+c/v work universally
bind = $mainMod, c, sendshortcut, CTRL+SHIFT, c, # Copy
bind = $mainMod, v, sendshortcut, CTRL+SHIFT, v, # Paste
# Clipse clipboard manager (much better than cliphist)
exec-once = clipse -listen
bind = $mainMod, p, exec, ghostty --class=clipse -e clipse
# Make clipse window float and look nice
windowrulev2 = float,title:(clipse)
windowrulev2 = center,title:(clipse)
windowrulev2 = size 800 600,title:(clipse)
The sendshortcut
command forwards the key combination to the focused application. So mod+c
becomes ctrl+c
in whatever app has focus. For paste, I use ctrl+shift+v
because most terminal apps expect that.
Clipse is a simple clipboard manager that stores everything in a history. Press mod+p
and get a floating terminal with your clipboard history.
Screenshot Tool: flameshot → grim + slurp
# i3 screenshots
bindsym $mod+Shift+s exec --no-startup-id "flameshot gui"
# Hyprland equivalent
bind = $mainMod SHIFT, s, exec, grim -g "$(slurp)" - | wl-copy
A quick aside, careful with your envs
This drove me crazy for the better part of an hour. Lutris launched perfectly from my terminal but clicking it in wofi did nothing. No error, no feedback, just silence.
The Real Problem
After creating a debug script to capture the environment, I found the issue:
# From terminal (working)
$ python3 --version
Python 3.13.7
$ which python3
/bin/python3
# From Hyprland launcher (broken)
Python version: Python 3.12.3
/home/tsoporan/.rye/py/cpython@3.12.3/lib/python3.12/site-packages
ModuleNotFoundError: No module named 'lutris'
The culprit: rye was setting up a Python 3.12 environment in my .zshrc. My terminal inherited this environment, but system Python 3.13 (where Lutris was actually installed) was what I thought I was using.
When launching from wofi, Hyprland processes didn’t load my .zshrc, so they tried to use rye’s Python 3.12 - which didn’t have Lutris installed.
Visual Consistency: Dracula Theme Throughout
Maintaining my beloved Dracula theme across components:
# Hyprland window borders
general {
col.active_border = rgba(6272A4ff) # Dracula blue
col.inactive_border = rgba(44475Aff) # Dracula comment
}
The Waybar CSS also follows the Dracula color scheme (shown in the earlier Waybar section).
Performance and Polish
Font Scaling for 4K
With fractional scaling, font sizes needed adjustment:
# Ghostty terminal
font-family = JetBrainsMono Nerd Font
font-size = 14 # Increased from default for 4K readability
Autostart Applications
# Essential services and applications
exec-once = waybar # Status bar
exec-once = hyprpaper # Wallpaper manager
exec-once = swaync # Notification daemon (replaced dunst)
exec-once = clipse -listen # Clipboard manager
exec-once = hyprpm reload -n # Load hy3 plugin
exec-once = hypridle # Idle management
exec-once = wl-paste -p -t text --watch wl-copy # Sync primary/clipboard
exec-once = dbus-update-activation-environment --all
What I Actually Gained
My Hyprland setup now feels pretty much like my old i3 workflow, but with bonuses:
- Improved 4K handling - it looks pretty crisp, and the weird picom/compositing issues seem to be gone
- Same muscle memory - every keybinding works identically
- Better clipboard behavior -
clipse
is nicer than what I was using before, it also supports images which is a benefit
The migration was surprisingly straight forward, took about 2hrs with Claude Code helping with the config translation and research (typically would spend a weekend on this!). If you’re interested in how I use AI tools for development, check out my post on Building with Claude Code: Dev Philosophy.
Updates
- I could not stand the layout management with Hyprland, but luckily found hy3 - highly recommended. This gives me back the tabbed/tiling toggle that I’ve grown accustomed to, e.g:
$mod+s - swaps between tabbed vs tiled mode
-
Swapped from
dunst
to swaync for notifications, just works with Wayland + some minor theme tweaks -
I had to update my scaling from auto to at least 1.5x in the hyprland config, otherwise some apps, which do support Wayland, look super tiny (e.g Chromium, Signal)
-
Added proper idle management with
hypridle
and screen locking withhyprlock
- both themed with Dracula colors -
Created a cheatsheet accessible via
mod+shift+tab
that covers all keybindings
System specs: Arch Linux, AMD Radeon RX 7800 XT, 4K display with fractional scaling