How to Create Custom Keymaps in Neovim v0.12 With Lua

Deep dive into Neovim keymap configuration using Lua. Explore vim.keymap.set(), mode handling, and extensible patterns for building maintainable and ergonomic editor workflows.

Published on: Wednesday, November 10, 2021

NOTE: The article was recently updated to reflect the changes introduced in the latest version of Neovim (currently v0.12+).

Neovim is a modern, extensible, and highly customizable text editor built as a fork of Vim. One of its major advantages over alternatives is the built-in Lua runtime. In addition to Lua's standard library, Neovim exposes an "editor libraryj" through the global vim namespace and a Lua-based API, enabling deep and flexible customization.

A comprehensive overview of everything Neovim can do is beyond the scope of this article. Instead, this guide focuses on a practical starting point: setting up custom keymaps using Lua.

A Brief Introduction to Lua

If you're unfamiliar with Lua and its nuances, Neovim provides comprehensive documentation for both the language and its integration. You can explore these in :h lua and :h luaref.

When setting up your own keymaps, you'll also want to refer to :h vim.keymap.

Keymaps can be defined in ~/.config/nvim/init.lua, ~/.config/nvim/lua/keymaps.lua, or any other module, as long as it resides within Neovim's runtime path (see :h 'runtimepath' for details).

For brevity, this section stays focused on keymaps. If you'd like a deeper understanding of Lua, here are a few curated resources:

Managing the Lua-based Keymaps

Now that you're familiar with Lua, you can define keymaps using :h vim.keymap.set(). This function accepts four parameters:

Note: It's worth reviewing :h :map-modes to better understand how Neovim handles different mapping modes.

  • {modes}: A string or list of strings representing Vim modes (see :h vim-modes).
  • {lhs}: The left-hand side of the mapping (the key sequence you press).
  • {rhs}: The right-hand side of the mapping, either a Lua string or a function executed when the mapping is triggered.
  • {opts}: A table of options (see :h map-arguments).

Here are a few examples:

local map = vim.keymap.set

map('i', 'jk', '<ESC>', { desc = 'Change to NORMAL mode' })
map('n', 'H', '<HOME>', { desc = 'Move to the beginning of the line' })
map('n', 'L', '<END>', { desc = 'Move to the end of the line' })

-- Call a function when a keymap is invoked
map('n', 'gD', vim.lsp.buf.declaration, { desc = 'Jump to the object declaration' })

For brevity, these examples only cover a small subset of what's possible. You're encouraged to build on them and define mappings that suit your workflow-how you personalize your Neovim setup is entirely up to your needs and preferences.

Parting Words

As demonstrated, building a personalized Neovim experience is straightforward, largely due to its embedded Lua runtime. Compared to Vimscript, Lua provides a more expressive and ergonomic interface for defining keymaps, making configuration both intuitive and maintainable.

This article only scratches the surface of what's possible. If you're looking to extend your setup further or explore real-world patterns, take a look at my dotfiles repository-specifically the Neovim keymaps. Use it as a reference, adapt what fits your workflow, and iterate on it to suit your own editing model.