DEV Community

Mark Robert Henderson
Mark Robert Henderson

Posted on

Neovim Config: from init.vim to init.lua

It's 9:30PM, I'm on vacation, and I drank an energy drink an hour ago. It's time to shave some yaks as hard as possible.

Of course I'm talking about my Neovim configuration file, and migrating it from the Vimscript-based init.vim to a Lua-based init.lua.

Why would you do this to yourself?

A few reasons:

  • init.lua is backwards compatible with init.vim, but not the other way around
  • Lua has applications outside of Vim, and this is a good excuse to familiarize myself with Lua more.
  • I haven't done any benchmarking whatsoever, but Lua is probably faster than Vimscript, right? I mean, Lua is pretty fast...
  • I wanted to try some Lua-based plugins like NvimTree
  • Oh, do you know Vimscript? Yeah, didn't think so.

The API docs for the Lua + Neovim are here:

Migration Strategy

My overall strategy was pretty simple: first wrap everything in vim.cmd([[ ... ]]) and then pluck things out individually as candidates for conversion to Lua.

The code that follows exemplifies this process, and highlights certain categories of settings and what the Lua code looked like in the end.


Vim's Lua APIs provide a convenience function vim.cmd that takes a string literal as its only argument. Anything in that string is evaluated as Vimscript. Kind of scary, but since we're controlling what goes into it it becomes extremely convenient for our migration.


    " the contents of your existing init.vim
Enter fullscreen mode Exit fullscreen mode

And you're off to the races... but where's the fun in that? Where's the Lua?

Settings and Options

The first low-hanging fruit is your Vimscript set directives like set wrap. You have three options here and it can be a little confusing as to which options live where. I hate to sound like one of those vim people, but :help is really good here.


This is for global commands, basically anything in the let g:*** Vimscript namespace. For example, let g:mapleader = " " becomes vim.g.mapleader = " "


This will likely be the bread and butter of your migration, as it's the most consistent 1:1 mapping of set options and Lua expressions. See for yourself:

vim.o.spell = "yes"
vim.o.splitbelow = true
vim.o.splitright = true
vim.o.swapfile = false
vim.o.synmaxcol = 30
Enter fullscreen mode Exit fullscreen mode

Note they even kept the weird "yes"-instead-of-true convention.


This is a special Lua table (read: hash map or associative array) that allows you to use more Lua-friendly syntax to set options.

For example, vim.opt.listchars is pretty nice:

vim.opt.listchars = {
  tab = "» ",
  trail = "·",
  extends = "›",
  precedes = "‹",
  nbsp = "␣",
Enter fullscreen mode Exit fullscreen mode

vim.api: Keymapping and Autocommands

For more advanced functionality like keymapping and autocommands, you have vim.api

vim.api.nvim_create_autocmd({ "FileType" }, {
    pattern = "rust",
    callback = function()
        vim.api.nvim_buf_set_option(0, "tabstop", 4)
        vim.api.nvim_buf_set_option(0, "shiftwidth", 4)
        vim.api.nvim_buf_set_option(0, "softtabstop", 4)
Enter fullscreen mode Exit fullscreen mode
vim.api.nvim_set_keymap('n', '<leader>s', ':split<CR>',
    { noremap = true })
Enter fullscreen mode Exit fullscreen mode


These are just some of the ideas and examples I was able to pull out of my experience in migrating to a Lua-based Neovim config. What do you think?

Another highlight of this adventure in yak-shaving is that it makes me review my old configuration code, allowing me to change, edit, and re-contextualize the ideas that I had and my previous intentions.

How about you? Have you done this same migration? Did I miss anything? Have you shaved any good yaks recently? I'd love to hear from you.

Top comments (0)