nvim-herdr-navigation is a vim-tmux-navigator alternative for Herdr: seamless
ctrl+h/j/k/l navigation between Neovim splits and Herdr panes.
Use the same Vim navigation keys everywhere. Move across Neovim windows first; when you reach a split edge, jump to the neighboring Herdr pane.
ctrl+h move left
ctrl+j move down
ctrl+k move up
ctrl+l move right
Prefer terminal playback? Use the asciinema recording:
asciinema play assets/demo.castThis project has two parts. Install both for full Neovim split and Herdr pane navigation.
herdr plugin install bojackduy/nvim-herdr-navigation/herdr-vim-navigatorVerify Herdr can see the plugin:
herdr plugin listEdit ~/.config/herdr/config.toml.
Disable Herdr's built-in direct pane focus bindings. The Vim-aware plugin must receive these keys first:
[keys]
focus_pane_left = ""
focus_pane_down = ""
focus_pane_up = ""
focus_pane_right = ""Bind ctrl+h/j/k/l to the Herdr plugin actions:
[[keys.command]]
key = "ctrl+h"
type = "plugin_action"
command = "local.vim-navigator.left"
description = "Vim-aware pane left"
[[keys.command]]
key = "ctrl+j"
type = "plugin_action"
command = "local.vim-navigator.down"
description = "Vim-aware pane down"
[[keys.command]]
key = "ctrl+k"
type = "plugin_action"
command = "local.vim-navigator.up"
description = "Vim-aware pane up"
[[keys.command]]
key = "ctrl+l"
type = "plugin_action"
command = "local.vim-navigator.right"
description = "Vim-aware pane right"Reload Herdr:
herdr server reload-configAdd this lazy.nvim spec:
return {
"bojackduy/nvim-herdr-navigation",
submodules = false,
cond = function()
return vim.env.HERDR_PANE_ID ~= nil
end,
event = "VeryLazy",
init = function(plugin)
vim.opt.rtp:prepend(plugin.dir .. "/nvim-herdr-navigation")
end,
config = function()
vim.schedule(function()
require("herdr-navigation").setup({
keybindings = {
left = "<C-h>",
down = "<C-j>",
up = "<C-k>",
right = "<C-l>",
},
})
end)
end,
}Important details for lazy.nvim and LazyVim users:
submodules = falseprevents lazy.nvim from cloning development-only reference submodules.initadds the nested Neovim plugin directory to the runtime path.event = "VeryLazy"plusvim.schedule()lets this plugin override LazyVim's default<C-h/j/k/l>window mappings inside Herdr.condkeeps this Neovim plugin disabled outside Herdr.
If you already use christoomey/vim-tmux-navigator, keep it active outside Herdr
and disabled inside Herdr:
return {
"christoomey/vim-tmux-navigator",
cond = function()
return vim.env.HERDR_PANE_ID == nil
end,
}vim-tmux-navigatorstyle navigation for Herdr and Neovim.- One set of
ctrl+h/j/k/lkeys for Neovim splits and Herdr panes. - Neovim-first behavior: move between splits before falling back to Herdr.
- Herdr-aware behavior: shell, agent, and non-Neovim panes move directly through Herdr.
- LazyVim-compatible mapping setup for default
<C-h/j/k/l>window keymaps. - Works alongside tmux navigation by loading
vim-tmux-navigatoronly outside Herdr. - No Herdr core patches required.
Use nvim-herdr-navigation if you want:
- Herdr pane navigation from Neovim.
- Neovim split navigation that falls back to Herdr at split edges.
- A Herdr equivalent of
vim-tmux-navigator. - LazyVim
ctrl+h/j/k/lnavigation that works across Herdr panes. - A terminal multiplexer navigation workflow for Herdr, Vim, and Neovim.
This repository contains two plugins that work together:
herdr-vim-navigator/: Herdr plugin actions forctrl+h/j/k/l. Herdr receives the key first, detects Vim or Neovim panes, and either forwards the key into Neovim or moves Herdr pane focus directly.nvim-herdr-navigation/: Neovim plugin. Neovim tries normal split navigation first. If focus is already at the requested split edge, it calls Herdr to move to the neighboring pane.
The herdr/ and nvim-tmux-navigation/ directories are reference submodules.
They are not required at runtime.
- Herdr
0.7.0or newer - Neovim
0.8or newer herdravailable on$PATHinside Neovimlazy.nvimor another Neovim plugin manager
Start Herdr, open a pane running Neovim, and create a few Neovim splits:
:vsplit
:splitExpected behavior:
- In Neovim,
ctrl+h/j/k/lmoves between Neovim splits first. - At a Neovim split edge, the same key moves to the neighboring Herdr pane.
- In a shell, agent, or non-Neovim pane, the same key moves Herdr pane focus.
- Outside Herdr, your existing tmux navigator or normal Neovim mappings can keep working.
This follows the two-sided design popularized by vim-tmux-navigator:
- Herdr receives
ctrl+h/j/k/lfirst. - The Herdr plugin checks whether the focused pane is running Vim or Neovim.
- If the focused pane is not Vim or Neovim, Herdr moves pane focus directly.
- If the focused pane is Vim or Neovim, Herdr forwards the original key into the pane.
- The Neovim plugin runs
wincmd h/j/k/l. - If Neovim focus did not change, the plugin runs
herdr pane focus --direction ... --pane $HERDR_PANE_ID.
The plugin creates these commands:
:HerdrNavigateLeft
:HerdrNavigateDown
:HerdrNavigateUp
:HerdrNavigateRightOptional compatibility aliases are available if you are replacing existing
vim-tmux-navigator command mappings:
require("herdr-navigation").setup({
tmux_compat_commands = true,
})That creates:
:TmuxNavigateLeft
:TmuxNavigateDown
:TmuxNavigateUp
:TmuxNavigateRightUse submodules = false in the lazy.nvim spec. The repository includes reference
submodules for development, but the Neovim plugin does not need them.
Check the live Neovim mapping:
:lua print(vim.inspect(vim.fn.maparg("<C-l>", "n", false, true)))The mapping should say desc = "Herdr navigate right" and should have a Lua
callback. If it says Go to Right Window or maps to <C-w>l, another config is
overriding the plugin. Use the lazy.nvim spec above with event = "VeryLazy" and
vim.schedule().
Herdr's built-in focus_pane_* keybindings are still active. Set them to empty
strings in ~/.config/herdr/config.toml, then run:
herdr server reload-configConfirm Neovim has the Herdr pane id:
:lua print(vim.env.HERDR_PANE_ID)Confirm Neovim can find Herdr:
:lua print(vim.fn.executable("herdr"))Confirm the Herdr command works for the current pane:
:lua vim.fn.jobstart({ "herdr", "pane", "focus", "--direction", "right", "--pane", vim.env.HERDR_PANE_ID }, { detach = true })The Herdr-side plugin uses a Vim process-name pattern based on
vim-tmux-navigator. Override it before launching Herdr if you use a custom
wrapper:
HERDR_VIM_NAVIGATOR_PATTERN='([^"[:space:]]+/)?(nvim|vim|my-vim-wrapper)' herdrYes. nvim-herdr-navigation provides the same style of pane-aware Vim navigation
for Herdr that vim-tmux-navigator provides for tmux.
Press ctrl+h/j/k/l at the edge of the Neovim split layout. The Neovim plugin
detects that focus did not move inside Neovim and asks Herdr to focus the
neighboring pane.
Yes. Use the lazy.nvim spec in this README. The VeryLazy event and
vim.schedule() are included so the Herdr mappings win over LazyVim's default
window-navigation mappings.
Yes. Keep vim-tmux-navigator enabled outside Herdr and disabled inside Herdr
with cond = function() return vim.env.HERDR_PANE_ID == nil end.
The recommended lazy.nvim spec only loads the Neovim plugin when
HERDR_PANE_ID exists. Outside Herdr, keep using your existing Neovim or tmux
navigation setup.
vim-tmux-navigator: the tmux and Vim workflow this project is inspired by.nvim-tmux-navigation: a Neovim-focused tmux navigation plugin used here as a reference.- Herdr: the terminal workspace and pane manager this plugin targets.
MIT. See LICENSE.
This project is relevant for searches such as Herdr Neovim navigation, Herdr Neovim navigator, vim-tmux-navigator for Herdr, Neovim split navigation, Herdr pane navigation, LazyVim ctrl h j k l, terminal multiplexer navigation, and Neovim pane focus from Herdr.
