HyprVim brings the power of Vim keybindings and motions to your Hyprland desktop environment.
HyprVim.Demo.mp4
Think of it as a lightweight, system-wide Vim mode for all of your applications.
Important
HyprVim now targets Hyprland's Lua plugin/configuration flow.
If you still need the old Hyprland .conf format, use the legacy-conf branch. New features and fixes target the Lua version.
📚 Full Reference: For a complete, searchable reference of all features, visit the Guide.
📰 Latest News: For the latest release information, visit the News.
Built on Hyprland’s native submap system, uses standard GUI application keyboard shortcut macros to emulate Vim-style navigation and text editing.
- 🚦 Vim Modes -
NORMAL,INSERT,VISUAL,V-LINE, andCOMMANDmodes - 🧭 Navigation - Character (
hjkl), word (w/b/e), line (0/$), paragraph ({}), page (Ctrl+d/u), document (gg/G) - ✂️ Operators - Delete (
d), change (c), yank (y) with motion and text object support - 📝 Text Objects - Inner/around word (
iw/aw), inner/around paragraph (ip/ap) - 🔢 Count Support - Repeat any motion or operator (e.g.,
5j,3dw,2yy) - 📌 Marks - Save and jump to positions across workspaces/monitors (
m{mark},`{mark}) - 📋 Registers - Multi-clipboard with named (
"a-z) and special registers ("0,"_,"/) - 🔍 Find/Search - Interactive search (
/,?,f,t,*,#) with next/previous (n/N) - 🔄 Replace - Character (
r) and string replacement (R) - 🔁 Surround - Wrap text with pairs (
gsfor word,Sin visual) - supports(),{},[],<div>, or custom with spaces - ↩️ Undo/Redo - Standard undo/redo (
u,Ctrl+r) - ⌨️ Command Mode - Execute commands (
:w,:q,:split,:float,:workspace,:reload, etc.) - 🗺️ Which-Key HUD - Shows all keybinds for submaps on entry. Press
SPACEto toggle (requireseww) - Open Vim/Nvim Anywhere - Press
SUPER + Nto open selected text in Vim/Nvim for complex editing. Save/close to paste.
Warning
Just like real Vim, you also need to know how to exit HyprVim: press SUPER + ESC or ALT + ESC
All extra configs for a better global Vim experience.
To use the extras, refer to their respective documentation.
| Tool | Description | Extra |
|---|---|---|
| Hyprland Basics | Hyprland keymap kickstart config for HyprVim (Resize, Move, Windows, etc) | extras/hyprland-basics |
| Keyd | System-wide key remaps and tap/hold layers | extras/keyd |
| Thunderbird | Keybinds for Vim driven navigation | extras/thunderbird |
| Tridactyl | Vim-style navigation for Firefox (advanced) | extras/tridactyl |
| Vimium | Vim-style navigation for web browsers (basic) | extras/vimium |
| Waybar Submap | Waybar submap visual Indicator | extras/waybar |
| WhichKey | WhichKey like display built using eww to see keybinds for submaps |
docs/guide/whichkey |
| Wl-kbptr | Keyboard-driven mouse cursor control on Wayland | extras/wl-kbptr |
If you'd like an extra config added, raise a feature request or put one together and send a pull request.
| Name | Description |
|---|---|
| Hyprland | Wayland compositor |
wl-clipboard |
Wayland clipboard utilities (wl-copy, wl-paste) |
| A terminal emulator | For the command-mode, replace-mode, find-mode, and help |
eww (optional) |
Widget system for the which-key HUD |
jq (optional) |
Required by the which-key HUD and manual-install updater |
socat (optional) |
Required by the which-key HUD daemon |
Warning
HyprVim is currently installed manually.
AUR package is planned. Arch users who prefer package-manager ownership should wait.
Due to the ongoing malware issue on the AUR, this is delayed till the AUR allows registrations once again.
Once the package is published, install hyprvim from the AUR with your preferred helper:
paru -S hyprvim
# or
yay -S hyprvimCreate a shim in your Hyprland lua plugins directory:
mkdir -p ~/.config/hypr/lua/plugins/hyprvim
cat > ~/.config/hypr/lua/plugins/hyprvim/init.lua <<'EOF'
-- Hyprvim bootstrap
local chunk, err = loadfile('/usr/share/hyprvim/init.lua')
if not chunk then
error(err)
end
return chunk()
EOFThen continue with Load HyprVim.
Manual git-checkout installs also need git, curl, and jq for the built-in updater. Update notifications use notify-send when available and fall back to a passive Hyprland notification.
Install HyprVim to ~/.local/share and create a shim in your Hyprland lua plugins directory:
git clone https://github.com/uhs-robert/hyprvim \
~/.local/share/hyprland/lua/plugins/hyprvim
mkdir -p ~/.config/hypr/lua/plugins/hyprvim
cat > ~/.config/hypr/lua/plugins/hyprvim/init.lua <<'EOF'
-- Hyprvim bootstrap
local path = os.getenv('HOME')
.. '/.local/share/hyprland/lua/plugins/hyprvim/init.lua'
local chunk, err = loadfile(path)
if not chunk then
error(err)
end
return chunk()
EOFAdd HyprVim to your ~/.config/hypr/hyprland.lua:
require("lua/plugins/hyprvim").setup()Tip
You may also pass a table of configuration settings to customize your experience.
Save and reload your Hyprland config:
hyprctl reloadTip
Verify installation: Press SUPER + ESC and you should enter NORMAL mode.
Package-managed installs handle updates through pacman or your AUR helper. Update HyprVim as you would any package in your package manager.
For git-checkout installs, HyprVim checks for updates on every Hyprland reload and notifies you via your desktop notification daemon. Clicking the notification applies the update and reloads automatically.
Manual git-checkout users can also run :update at any time from NORMAL mode to apply manually. Package-managed installs should use pacman or an AUR helper instead.
The default channel is "stable" (latest GitHub release). Configure it in your setup() call:
updates = {
channel = "stable", -- latest release (default)
-- channel = "nightly", -- git HEAD
-- channel = "v1.2.3", -- pinned release tag
-- channel = "abc1234", -- pinned commit SHA
-- channel = "off", -- disable update checks
},Note
Update notifications require notify-send (libnotify) and a compatible notification daemon (dunst, mako, or swaync).
Without one of those, manual git-checkout installs show a passive Hyprland notification instead and you must use :update to apply.
📚 Full Reference: For a complete usage guide, visit the Guide.
Press SUPER + ESCAPE (or your configured leader key + activation key) to enter NORMAL mode.
- Enter NORMAL mode:
SUPER + ESC - See all keybindings: Press
ghto show help - Navigate: Use
hjkl,w,b,eto move around - Select text/items: Press
vfor visual mode, then navigate to select - Edit: Use operators like
d,c,ywith motions or in visual mode - Return to insert: Press
i,a, or other insert commands - Exit Vim mode: Press
SUPER + ESCagain
Save and jump to window positions across workspaces and monitors using m{mark} to set, `{mark} to jump.
📖 Learn more: Marks guide
Multi-clipboard management with named registers ("a - "z) and special registers ("" unnamed, "0 yank, "_ black hole). Use "{register}{operation} (e.g., "ayy to yank to register a, "ap to paste from register a).
📖 Learn more: Registers guide
Press : in NORMAL mode to execute Vim-style commands. Common commands: :w (save), :q (quit), :wq (save & quit), :split (split window), :float [on|off] (floating), :fullscreen [maximized|fullscreen], :ws <N|name:Web|empty> (switch workspace), :move N (send window to workspace), :monitor <dir|name> (focus monitor), :focus class:firefox (focus window), :rename <name> (rename workspace), :special <name> (scratchpad), :swap <l|r|u|d>, :resize N, :opacity V, :prop <name> <value>, :reload, :update, :!cmd (shell). Full reference: :help.
📖 Learn more: Command Mode guide
HyprVim includes pragmatic pass-through bindings in NORMAL mode for better GUI interaction: TAB, RETURN, CTRL+V/X/A/S/W/Z.
This enables dialog navigation and clipboard operations without constantly switching to INSERT mode.
Warning
These may trigger unwanted actions in text editors. Use i to enter INSERT mode when editing text, or override bindings via the keymaps option.
HyprVim offers many different options to choose from. Have fun customizing with setup()!
🍦 Default Options
require("hyprvim").setup({
keys = {
leader = "SUPER",
activate = "ESCAPE",
exit = "SHIFT + ESCAPE",
},
applications = {
terminal = "kitty",
term_flags = nil, -- add entries here for custom terminal launch flags
lock = "hyprlock",
editor = "nvim", -- `vim` or `nvim`
},
notifications = {
all = false, -- Enable to bypass settings below and just enable all
marks = false,
warnings = true,
errors = true,
},
updates = {
channel = "stable", -- "stable" (latest release), "nightly" (git HEAD), "off", or a tag/commit SHA to pin
},
enable_debug = false,
max_count = 1000,
which_key = {
enabled = true, -- This requires eww
delay_ms = 0, -- 0 = instant, else delayed a bit (200 gives you some breathing room)
vim_delay_ms = 300,
position = "bottom-right",
auto_show = {
disabled = {
"NORMAL",
"VISUAL",
"V-LINE",
"INSERT",
},
enabled = nil, -- nil enables all except those in disabled. You could make disabled = nil and then it would work the opposite.
},
},
-- keymaps = {
-- NORMAL = {
-- { "w", function() my_fn() end, { desc = "My word" } }, -- override a built-in bind
-- { "SUPER + x", function() end }, -- add a new bind
-- },
-- },
-- commands = {
-- browser = function() hl.dispatch(hl.dsp.exec_cmd("firefox")) end, -- add a new :command
-- q = function() my_custom_quit() end, -- override a built-in
-- },
})📖 Learn more: Configuration guide
For AUR installs, remove the package and the Hyprland plugin shim:
paru -R hyprvim
# or
yay -R hyprvim
rm -rf ~/.config/hypr/lua/plugins/hyprvim
hyprctl reloadFor manual git-checkout installs, remove the shim and cloned plugin directory:
rm -rf ~/.config/hypr/lua/plugins/hyprvim
rm -rf ~/.local/share/hyprland/lua/plugins/hyprvim
hyprctl reloadNote
Any temporary files created by HyprVim for state management are automatically cleaned up on reboot.
To see which Vim mode you're currently in, add the Hyprland submap module to your Waybar configuration.
This displays the active submap in your status bar.
WhichKey requires eww to display. It is an optional feature that is disabled by default.
We highly recommend using WhichKey to learn the keybindings. It also displays active marks and works with your other submaps too.
You can find the demo and setup instructions in the Guide for WhichKey.
On that note, check out all the extras too! This is just the tip of the iceberg, you never know what you might find.
- No macros
- No visual block mode (
Ctrl+vorCtrl+q) - Limited text object support (word and paragraph only)
- Registers/marks are stored in tmpfs (not persistent across reboots)
- Find operations use interactive prompts and application find dialogs
- Effectiveness depends on application supporting standard keyboard shortcuts
Warning
HyprVim is designed for GUI applications first. Terminals behave differently.
Terminals often use a different set of keyboard shortcuts so motions may not work as expected.
However shells (bash, zsh, etc) usually ship a vi mode. Try using that instead.
If you must use it in the shell, some actions may work but your mileage will vary.
Pass a keymaps table to setup() to override or extend the binds in any built-in submap.
Entries where a key matches a built-in bind will replace them; new keys are appended.
require("hyprvim").setup({
keymaps = {
NORMAL = {
{ "w", function() my_custom_word() end, { desc = "custom word" } }, -- override built-in w
{ "SUPER + X", function() my_extra_action() end }, -- add a new bind
{ "SUPER + M", hl.dsp.submap("my-submap"), { desc = "my submap" } }, -- or add a submap dispatch shortcut to one of your own
},
VISUAL = {
{ "y", function() my_custom_yank() end, { desc = "custom yank" } },
},
},
})Because keymaps are evaluated at setup() call time, inside your hyprland.lua, any functions that you have defined there are in scope.
Built-in submap names: "NORMAL", "VISUAL", "V-LINE", "INSERT", "G-MOTION", "G-VISUAL".
Pass a commands table to setup() to add new :commands or override built-ins.
require("hyprvim").setup({
commands = {
browser = function() hl.dispatch(hl.dsp.exec_cmd("firefox")) end,
files = function() hl.dispatch(hl.dsp.exec_cmd("thunar")) end,
myaction = function() hl.exec_cmd("my-script") end,
},
})Custom commands appear in tab-completion alongside the built-in ones.
You can also reference HyprVim submaps in your own keybinds after sourcing HyprVim and use HyprVim scripts in your own keybinds. Some examples are included in Hyprland basics.
If you make an enhancement that you think would benefit the community then please submit a pull request and I'll be happy to review it.
