home / skills / kriscard / kriscard-claude-plugins / lazy-nvim-optimization

This skill helps optimize Neovim startup by applying lazy-loading patterns with lazy.nvim for selective plugin loading and faster startup.

npx playbooks add skill kriscard/kriscard-claude-plugins --skill lazy-nvim-optimization

Review the files below or copy the command above to add this skill to your agents.

Files (4)
SKILL.md
3.9 KB
---
name: lazy-nvim-optimization
description: >-
  Profiles Neovim startup performance and optimizes lazy.nvim plugin loading with
  targeted lazy-loading specs, priorities, and event triggers. Contains profiling
  workflow and bottleneck checklists. Make sure to use this skill whenever the user
  mentions slow neovim startup, neovim takes time to load, plugin profiling,
  lazy-loading, lazy.nvim config, startup time optimization, or neovim performance
  — even if the user doesn't say "lazy.nvim" explicitly.
version: 0.1.0
---

# 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

```bash
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

```vim
: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

```bash
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 = 1000` so 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:**
```lua
-- 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 strategy
- **`references/profiling-guide.md`** - Advanced profiling techniques

Overview

This skill helps optimize Neovim startup and runtime by applying lazy.nvim–specific lazy-loading patterns, profiling, and configuration tactics. It focuses on reducing startup time, deferring non-critical work, and making plugin loading predictable and reproducible. Use it when you want a fast, responsive Neovim with full plugin functionality.

How this skill works

The skill inspects plugin declarations and recommends triggers (cmd, keys, ft, event, or lazy) to avoid loading plugins at startup. It uses profiling (nvim --startuptime and :Lazy profile) to find slow sources, heavy setup() calls, and unnecessary early loads, then suggests concrete fixes: defer setup, change triggers, or mark dependencies lazy. It also covers lockfile workflows to ensure reproducible plugin versions.

When to use it

  • After noticing slow startup (>50–100ms) or regressions in responsiveness
  • When adding new plugins and you want to avoid startup cost
  • When converting an existing config to lazy.nvim or improving lazy-loading
  • Before committing plugin changes to ensure reproducible versions
  • When profiling shows setup() or require() calls taking >5–10ms

Best practices

  • Measure first: use nvim --startuptime and :Lazy profile before changes
  • Lazy-load non-essential plugins by cmd/keys/ft/event instead of loading at startup
  • Keep colorschemes and critical UI plugins with high priority (e.g., priority = 1000)
  • Defer heavy setup() calls via VeryLazy, event triggers, or init/config split
  • Avoid synchronous system calls during startup; defer them with vim.defer_fn
  • Commit lazy-lock.json to version control for reproducible installs

Example use cases

  • Make Telescope load only on :Telescope and common keymaps, with fzf-native as a dependency
  • Load nvim-cmp on InsertEnter so completion costs nothing at startup
  • Split LSP configs into filetype-specific modules so servers load only for edited languages
  • Defer UI niceties like todo-comments or noice.nvim to VeryLazy to improve first paint
  • Conditionally enable plugins (e.g., markdown preview) only when external binaries exist

FAQ

How do I find which plugin is causing slow startup?

Run nvim --startuptime startup.log and :Lazy profile. Look for require()/sourcing lines >10ms and plugins with large setup times.

When should I not lazy-load a plugin?

Do not lazy-load things required for immediate UX: colorschemes, core treesitter parsers for syntax, or statusline components shown on first render.