lazy-nvim-optimization
Lazy.nvim Optimization
Diagnose and fix Neovim startup performance through profiling and targeted lazy-loading.
Profiling Workflow
When a user reports slow startup, follow this sequence — don't skip to solutions.
Step 1: Measure Baseline
nvim --startuptime startup.log && tail -1 startup.log
Compare against targets:
- < 30ms: Excellent
- 30-50ms: Good
- 50-100ms: Acceptable
-
100ms: Needs work
Step 2: Identify Slow Plugins
:Lazy profile
Look for plugins with load time > 10ms. These are your optimization targets. Sort by time, not alphabetically.
Also check the startup log for:
- Files taking > 10ms to source
setup()functions taking > 5ms- Synchronous operations blocking startup
Step 3: Apply Lazy-Loading
For each slow plugin, choose the right trigger:
| Plugin Type | Trigger | Example |
|---|---|---|
| Has clear commands | cmd |
cmd = "Telescope" |
| Accessed via keybindings | keys |
keys = { "<leader>e" } |
| Language-specific | ft |
ft = { "rust", "go" } |
| Needed after UI renders | event = "VeryLazy" |
UI enhancements |
| Needed when editing | event = "BufReadPost" |
Git signs, diagnostics |
| Needed in insert mode | event = "InsertEnter" |
Completion, autopairs |
| Only used as dependency | lazy = true |
plenary.nvim |
Step 4: Verify Improvement
nvim --startuptime startup-after.log && tail -1 startup-after.log
Compare total times. Then :Lazy profile to verify plugins load when expected.
What NOT to Lazy-Load
These need to load at startup — don't fight it:
- Colorscheme — Set
priority = 1000so it loads first. Visible flash if deferred. - Treesitter — Needed for syntax highlighting immediately. Deferring causes flicker.
- Statusline — Visible at startup. Deferring causes layout shift.
- which-key — Only if it shows on startup. Otherwise can lazy-load.
- LSP base setup — Though individual servers can lazy-load by filetype.
Common Bottlenecks
Synchronous system calls at startup:
-- Bad: blocks startup
vim.fn.system("git status")
-- Good: defer it
vim.defer_fn(function()
vim.fn.system("git status")
end, 100)
Loading all LSP servers at once: Consider loading LSP servers per-filetype instead of all at startup. Each server you don't load saves 5-15ms.
Plugins without any trigger:
A bare { "plugin/name" } spec loads at startup. Always add a trigger unless the plugin genuinely needs immediate availability.
Performance Checklist
Startup
- Colorscheme has
priority = 1000 - File explorers load on cmd/keys
- Git interfaces load on cmd/keys
- Completion loads on
InsertEnter - Language plugins load on filetype
- UI enhancements load on
VeryLazy
Runtime
- Telescope uses fzf-native extension
- Treesitter parsers installed only for used languages
- LSP servers only for filetypes you use
- No plugins duplicating built-in 0.10+ features
Config
- No synchronous system calls at startup
-
setup()only runs when plugins load - Keymaps defined with plugin lazy-loading
- Auto-commands use specific events, not
"*"
Reference Files
For detailed information:
references/lazy-loading-decision-tree.md- Decision tree for choosing lazy-loading strategyreferences/profiling-guide.md- Advanced profiling techniques