Skip to main content

Plugin Management with lazy.nvim

lazy.nvim is the modern Neovim plugin manager. It supports lazy loading (plugins load only when needed), declarative specs in Lua tables, and a lock file for reproducibility.

Core Idea

lazy.nvim installs and loads plugins defined as Lua tables. Plugins are lazy-loaded by event, command, filetype, or key press — so your startup stays fast regardless of how many plugins you have.

Bootstrap (Auto-Install lazy.nvim)

Add this to the TOP of your init.lua before any require() calls:

~/.config/nvim/init.lua
-- Bootstrap lazy.nvim
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not vim.loop.fs_stat(lazypath) then
vim.fn.system({
"git", "clone",
"--filter=blob:none",
"https://github.com/folke/lazy.nvim.git",
"--branch=stable",
lazypath,
})
end
vim.opt.rtp:prepend(lazypath)

-- Set leader BEFORE lazy
vim.g.mapleader = " "
vim.g.maplocalleader = "\\"

-- Load config
require("config.options")
require("config.keymaps")
require("config.autocmds")

-- Setup plugins
require("lazy").setup("plugins", {
-- lazy.nvim options
change_detection = { notify = false },
ui = { border = "rounded" },
performance = {
rtp = {
disabled_plugins = {
"gzip", "matchit", "matchparen",
"netrwPlugin", "tarPlugin", "tohtml",
"tutor", "zipPlugin",
},
},
},
})

Plugin Spec Format

When you call require("lazy").setup("plugins", ...), lazy.nvim auto-loads all .lua files from lua/plugins/:

lua/plugins/
├── init.lua ← optional: explicit list
├── ui.lua ← theme, statusline
├── lsp.lua ← LSP
├── telescope.lua ← file search
├── treesitter.lua ← syntax
└── git.lua ← git

Plugin Spec Table

{
"author/plugin-name", -- GitHub repo
version = "*", -- latest stable tag
-- or:
branch = "main",
commit = "abc123",

-- When to load:
event = "BufEnter", -- load on event
event = { "BufReadPre", "BufNewFile" },
cmd = "MyCommand", -- load on command
ft = "lua", -- load for filetype
keys = { "<leader>ff" }, -- load on keypress

-- Dependencies:
dependencies = {
"nvim-lua/plenary.nvim",
{ "another/dep", lazy = true },
},

-- Configuration:
opts = { ... }, -- passes to plugin's setup()
config = function(_, opts) -- custom setup function
require("plugin").setup(opts)
end,

-- Build steps:
build = ":TSUpdate", -- run after install/update

-- Priority (for colorschemes):
priority = 1000,
}

A Complete Plugin File Example

~/.config/nvim/lua/plugins/ui.lua
return {
-- Colorscheme
{
"folke/tokyonight.nvim",
lazy = false, -- load immediately (colorscheme)
priority = 1000, -- load before everything else
opts = {
style = "night",
transparent = false,
terminal_colors = true,
},
config = function(_, opts)
require("tokyonight").setup(opts)
vim.cmd("colorscheme tokyonight")
end,
},

-- Status line
{
"nvim-lualine/lualine.nvim",
event = "VeryLazy",
dependencies = { "nvim-tree/nvim-web-devicons" },
opts = {
options = {
theme = "tokyonight",
component_separators = "|",
section_separators = "",
},
},
},

-- which-key
{
"folke/which-key.nvim",
event = "VeryLazy",
opts = { plugins = { spelling = true } },
},

-- indent guides
{
"lukas-reineke/indent-blankline.nvim",
main = "ibl",
event = { "BufReadPost", "BufNewFile" },
opts = {},
},

-- Better notifications
{
"rcarriga/nvim-notify",
lazy = false,
opts = {
timeout = 3000,
max_height = function() return math.floor(vim.o.lines * 0.75) end,
max_width = function() return math.floor(vim.o.columns * 0.75) end,
},
config = function(_, opts)
require("notify").setup(opts)
vim.notify = require("notify")
end,
},

-- Buffer tabs (bufferline)
{
"akinsho/bufferline.nvim",
event = "VeryLazy",
dependencies = { "nvim-tree/nvim-web-devicons" },
opts = {
options = {
diagnostics = "nvim_lsp",
always_show_bufferline = false,
},
},
},
}

lazy.nvim Commands

:Lazy         → open the lazy.nvim UI
:Lazy install → install missing plugins
:Lazy updateupdate all plugins
:Lazy sync → install + update + clean
:Lazy clean → remove unused plugins
:Lazy check → check for updates
:Lazy profile → show startup time profile
:Lazy log → show change log

Lazy Loading Events

EventUse case
"BufEnter"Any buffer is entered
"BufReadPre"Before reading a buffer
{"BufReadPre", "BufNewFile"}Common: when editing any file
"InsertEnter"When entering insert mode
"VeryLazy"After everything else loads
"LspAttach"When LSP attaches

The Lock File

lazy.nvim generates ~/.config/nvim/lazy-lock.json — a snapshot of exact plugin commit hashes. Commit this to version control for reproducibility.

# Track in git for reproducibility
cd ~/.config/nvim
git add lazy-lock.json
git commit -m "lock plugin versions"

What's Next