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 update → update 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
| Event | Use 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"