Neovim with Lua

The introduction of Lua as a first-class language in Neovim was a turning point for the entire ecosystem. Where Vimscript often felt like a language you endured, Lua is one you can genuinely enjoy -- it brings clean syntax, real data structures, and the kind of performance that makes complex configurations feel instant. If you've ever hit the limits of Vimscript or wondered how modern Neovim plugins achieve so much, this chapter will show you the foundation they're built on.

Neovim natively supports Lua as a first-class configuration language. Lua is faster, more readable, and provides access to the full Neovim API. This chapter covers everything you need to migrate from Vimscript to Lua.

What you'll learn in this chapter:

  • Set up an init.lua and organize your Neovim configuration into modules

  • Translate common Vimscript settings and mappings into Lua equivalents

  • Use vim.api to create autocommands and user commands in Lua

  • Call Vimscript functions and commands from Lua when needed

Why Lua?

  • Performance: Lua is significantly faster than Vimscript

  • Readability: Cleaner syntax, proper data structures, functions as first-class citizens

  • Ecosystem: The modern Neovim plugin ecosystem is built on Lua

  • API access: Full access to Neovim's API (vim.api.*)

  • LSP & Treesitter: Built-in features are configured in Lua

Getting Started: init.lua

Neovim looks for configuration in this order:

  1. ~/.config/nvim/init.lua

  2. ~/.config/nvim/init.vim

You can only have one (see Chapter 10: Configuring Vim & Neovim for Vimscript-based configuration). To start with Lua:

Project Structure

A well-organized Neovim config:

Your init.lua then just requires these modules (the plugins/ directory is managed by lazy.nvim -- see Chapter 12: Plugin Management):

Setting Options: vim.opt

The vim.opt table replaces :set commands:

Vimscript to Lua Translation

Vimscript
Lua

set number

vim.opt.number = true

set nonumber

vim.opt.number = false

set shiftwidth=4

vim.opt.shiftwidth = 4

set path+=**

vim.opt.path:append('**')

let g:mapleader = ' '

vim.g.mapleader = ' '

let b:var = 1

vim.b.var = 1

Key Mappings: vim.keymap.set

Options for vim.keymap.set

Option
Default
Description

noremap

true

Non-recursive mapping

silent

false

Don't show command in command line

desc

nil

Description (shows in which-key)

buffer

nil

Buffer-local mapping

expr

false

Mapping is an expression

remap

false

Allow recursive mapping

Autocommands: vim.api.nvim_create_autocmd

User Commands: vim.api.nvim_create_user_command

Useful Vim API Functions

Calling Vimscript from Lua

You can always fall back to Vimscript when needed (for more advanced scripting techniques, see Chapter 21: Scripting):

Summary

Lua is the modern foundation for Neovim configuration. By using vim.opt for settings, vim.keymap.set for mappings, and vim.api for autocommands and user commands, you gain cleaner syntax, better performance, and full access to Neovim's API. A well-organized init.lua with modular files keeps your configuration maintainable as it grows.

Key takeaways:

  • vim.opt replaces :set, vim.g replaces :let g:, and vim.keymap.set replaces :map -- all with better ergonomics and type safety.

  • Organize your configuration into separate modules (options.lua, keymaps.lua, autocmds.lua) loaded from a central init.lua.

  • vim.api.nvim_create_autocmd and vim.api.nvim_create_user_command provide structured, Lua-native alternatives to Vimscript autocommands and commands.

  • You can always call Vimscript from Lua using vim.cmd or vim.fn when a Lua equivalent is not available.

Exercises

  1. Create an init.lua from scratch -- Back up your existing configuration. Create a new ~/.config/nvim/init.lua that sets number, relativenumber, expandtab, shiftwidth=4, and termguicolors using vim.opt.

  2. Define key mappings in Lua -- Set the leader key to space, then create mappings to save the current file with <leader>w and to clear search highlights with <Esc>.

  3. Convert a Vimscript autocommand to Lua -- Take the following Vimscript and rewrite it in Lua using vim.api.nvim_create_autocmd:

  4. Create a custom user command -- Write a Lua user command called :Today that inserts the current date on the line below the cursor.

  5. Use vim.fn to call a Vimscript function -- Write a Lua snippet that uses vim.fn.expand to print the full path of the current file and vim.fn.line to print the total number of lines.

Last updated

Was this helpful?