vimtricks.wiki Concise Vim tricks, one at a time.

How do I set window-local and buffer-local options correctly in Neovim Lua config using vim.wo and vim.bo?

Answer

vim.wo.number = true / vim.bo.tabstop = 2

Explanation

Neovim's Lua API exposes three namespaces for setting options with the correct scope: vim.wo for window-local options, vim.bo for buffer-local options, and vim.o (or vim.go) for global options. Using the wrong namespace for an option either silently does nothing or sets the wrong scope.

How it works

  • vim.wo (window-local): options like number, relativenumber, signcolumn, wrap, foldlevel
  • vim.bo (buffer-local): options like tabstop, shiftwidth, expandtab, filetype, readonly
  • vim.o (global): options like hidden, clipboard, termguicolors, mouse
  • vim.opt: a smarter accessor that infers scope and supports +=/-= operators

Example

-- Set options with correct scope
vim.wo.number = true          -- enable line numbers for this window
vim.wo.signcolumn = 'yes'    -- always show sign column in this window

vim.bo.tabstop = 2           -- 2-space tabs for this buffer
vim.bo.shiftwidth = 2
vim.bo.expandtab = true      -- use spaces, not tabs

vim.o.hidden = true          -- allow switching buffers without saving

Tips

  • vim.opt is often preferred in init.lua since it handles scope automatically and supports +=, -=, ^= like Vimscript set option+=value
  • Use vim.wo and vim.bo in FileType autocmds or LspAttach callbacks to scope settings tightly
  • Setting a buffer-local option via vim.wo will silently do nothing; always match the option to its correct namespace
  • Run :h options.txt and look for local to window or local to buffer tags to identify the correct scope

Next

How do I enable matchit so % jumps between if/else/end style pairs?