C.W.K.
Stream
Lesson 02 of 04 · published

Key Mappings — Make the Keyboard Yours

~13 min · vim, keymaps, leader

Level 0Trapped
0 XP0/35 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete

The leader key

The leader is your personal namespace for shortcuts. You pick a key (Space is the most popular choice — it's huge, ergonomic, and not used in Normal mode for anything important), and then build up your own command vocabulary on top of it: <leader>w to save, <leader>ff to find files, <leader>gs for git status. Every mapping you add is a sentence you've taught your hands.

The map families

Vim has a per-mode mapping system. nmap applies in Normal mode; imap in Insert; vmap in Visual; cmap in Command-line. Always prefer the noremap variants (nnoremap, inoremap, etc.) — they treat the right-hand side literally instead of recursively expanding it through other mappings, which is how you avoid infinite loops.

Lua's neater equivalent

In Neovim Lua, vim.keymap.set(mode, lhs, rhs, opts) replaces the whole map family. opts is a table where you can set desc (which-key plugins display this), silent = true, noremap = true (default in vim.keymap.set), buffer = N for buffer-local maps.

The mappings every config has

A small canon of mappings shows up in nearly every config because they fix real annoyances: clear search highlight on Esc, jk to escape Insert, leader-w to save, leader-q to quit, Ctrl-h/j/k/l for split navigation (override Ctrl-w prefix), n / N centered with zz, J / K in Visual mode to move lines.

Add mappings reactively, not proactively. Don't copy a 200-line keymap file from someone's dotfiles. When you find yourself typing the same sequence twice in one day, then map it. The best keymap is the one you remember without checking, which means it came from a need you actually felt.

Code

Set the leader and the canonical Insert escape·vim
" Vimscript
let mapleader = " "

inoremap jk <Esc>
inoremap jj <Esc>
Same in Lua·lua
vim.g.mapleader = " "
vim.g.maplocalleader = " "

vim.keymap.set("i", "jk", "<Esc>")
vim.keymap.set("i", "jj", "<Esc>")
The starter keymap pack — Vimscript·vim
" Save / quit / config-edit
nnoremap <leader>w :w<CR>
nnoremap <leader>q :q<CR>
nnoremap <leader>x :x<CR>

" Clear search highlight
nnoremap <Esc> :nohlsearch<CR>

" Window navigation
nnoremap <C-h> <C-w>h
nnoremap <C-j> <C-w>j
nnoremap <C-k> <C-w>k
nnoremap <C-l> <C-w>l

" Center after search / scroll
nnoremap n nzzzv
nnoremap N Nzzzv
nnoremap <C-d> <C-d>zz
nnoremap <C-u> <C-u>zz

" Move visual selection up/down
vnoremap J :m '>+1<CR>gv=gv
vnoremap K :m '<-2<CR>gv=gv

" Keep cursor in place when joining
nnoremap J mzJ`z

" System-clipboard yank/paste with leader
nnoremap <leader>y "+y
vnoremap <leader>y "+y
nnoremap <leader>p "+p

" Black-hole delete
nnoremap <leader>d "_d
vnoremap <leader>d "_d
The starter keymap pack — Lua·lua
local map = vim.keymap.set

-- Save / quit
map("n", "<leader>w", "<cmd>w<CR>", { desc = "Save file" })
map("n", "<leader>q", "<cmd>q<CR>", { desc = "Quit" })
map("n", "<leader>x", "<cmd>x<CR>", { desc = "Save and quit" })

-- Clear search highlight
map("n", "<Esc>", "<cmd>nohlsearch<CR>", { desc = "Clear search" })

-- Window navigation
map("n", "<C-h>", "<C-w>h", { desc = "Move to left split" })
map("n", "<C-j>", "<C-w>j", { desc = "Move to lower split" })
map("n", "<C-k>", "<C-w>k", { desc = "Move to upper split" })
map("n", "<C-l>", "<C-w>l", { desc = "Move to right split" })

-- Center after search / scroll
map("n", "n", "nzzzv")
map("n", "N", "Nzzzv")
map("n", "<C-d>", "<C-d>zz")
map("n", "<C-u>", "<C-u>zz")

-- Move visual selection up/down
map("v", "J", ":m '>+1<CR>gv=gv")
map("v", "K", ":m '<-2<CR>gv=gv")

-- Black-hole delete (preserves yank)
map({ "n", "v" }, "<leader>d", "\"_d", { desc = "Delete without yanking" })

External links

Exercise

Add the starter keymap pack to your config. Reload Neovim. Test each one: <Space>w saves, jk in Insert mode escapes, Ctrl-h/j/k/l moves between splits (open one with :vs first). Then add one personal mapping for something you do constantly — for example nnoremap <leader>sv :source $MYVIMRC<CR> to reload the config. Earn each mapping; don't import them in bulk.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.