Search docs... /
GitHub
Neovim 0.12+ Native Plugin Manager

Overview & Quickstart

pack.nvim combines Neovim 0.12's native C-powered vim.pack package management with a modern floating dashboard, precompiled ftdetect bytecode cache, and non-blocking background git probes.

Recommended Quickstart (init.lua)
-- 1. Enable Neovim's built-in bytecode loader (Line 1)
vim.loader.enable()

-- 2. Bootstrap pack.nvim with native vim.pack
vim.pack.add({ { src = "https://github.com/igmrrf/pack.nvim", branch = "main" } })
vim.cmd.packadd("pack.nvim")

-- 3. Initialize pack.nvim with options and plugin specs
require("pack").setup({
  plugins = {
    { "igmrrf/pack.nvim" }, -- pack manages itself
    { import = "plugins" },  -- imports lua/plugins/*.lua
  },
})

✅ Requirements

  • Neovim 0.12+ (delegates all cloning and lockfile ownership to vim.pack)
  • git on your system PATH

⚡ Key Highlights

  • Native C Engine: All plugins reside under <stdpath("data")>/site/pack/core/opt.
  • Bytecode Cache: Pre-compiles filetype detectors into pack_ftdetect_cache.lua.
  • Zero Dependencies: Under 2,000 lines of pure, rock-solid Lua code.
Bootstrapping

Basic Fallback Bootstrap

Standard vim.uv.fs_stat fallback snippet with automatic git cloning if pack.nvim is missing from disk.

fs_stat + git clone Pattern
-- Ensure pack.nvim is installed before running setup
local packpath = vim.fn.stdpath("data") .. "/site/pack/core/opt/pack.nvim"
if not (vim.uv or vim.loop).fs_stat(packpath) then
  local out = vim.fn.system({
    "git", "clone", "--filter=blob:none", "--branch=main",
    "https://github.com/igmrrf/pack.nvim.git", packpath,
  })
  if vim.v.shell_error ~= 0 then
    vim.api.nvim_echo({ { "Failed to clone pack.nvim:\n" .. out, "ErrorMsg" } }, true, {})
    return
  end
end
vim.opt.rtp:prepend(packpath)

require("pack").setup({
  plugins = { { "igmrrf/pack.nvim" } }
})
Neovim 0.12 Native

Native vim.pack.add Bootstrap

Minimal bootstrapping pattern utilizing Neovim 0.12's native vim.pack.add API.

Native vim.pack Bootstrap
-- 1. Enable bytecode cache
vim.loader.enable()

-- 2. Bootstrap pack.nvim natively
vim.pack.add({ { src = "https://github.com/igmrrf/pack.nvim", branch = "main" } })
vim.cmd.packadd("pack.nvim")

-- 3. Initialize pack.nvim
require("pack").setup({
  plugins = { { "igmrrf/pack.nvim" } }
})
Interactive Component

Live :Pack Dashboard Simulator

Try the floating terminal interface directly in your browser. Switch tabs, inspect specs with K, or run simulated updates.

NVIM v0.12.0-dev — :Pack Dashboard
vim.pack online
📦 pack.nvim manager
Plugin Name Status Load Trigger / Source Load Time
3 plugins have pending upstream commits. Press U to update all or s for selected.
Controls:
Interactive Spec Builder

Config Generator

Customize setup parameters and select preset plugins to generate valid Neovim 0.12 Lua configuration.

Options

Preset Specs

init.lua
-- Lua code generated dynamically...
Architecture

Core Capabilities & Engine

Deep dive into pack.nvim's native C integration and performance design.

Native C Core Engine

Delegates cloning, branch checkout, revision pinning, and lockfile creation to Neovim 0.12's C-native vim.pack backend under <stdpath("data")>/site/pack/core/opt.

Native vim.pack.add() engine

Precompiled ftdetect Cache

Pre-compiles lazy plugins' filetype detectors into a single cache block (pack_ftdetect_cache.lua) sourced at startup so filetypes match instantly before plugin scripts load.

Instant filetype detection

Async Read-Only Git Probes

Non-blocking background concurrency-limited git probes via vim.system provide live outdated commit counts and commit diff previews without UI stutter.

Background non-blocking updates

Native Lockfile Ownership

Native nvim-pack-lock.json holds canonical commit revisions. `:Pack restore` rolls every plugin back to lockfile state, while `:Pack repair` re-aligns hashes.

100% reproducible environments
Guide

Lazy Loading Triggers Guide

Learn how to defer plugin load times until triggered by commands, filetypes, keymaps, autocmd events, or patterns.

Lazy Loading Examples (examples/02_lazy_loading.lua)
require("pack").setup({
  plugins = {
    -- 1. Load on Command
    {
      "nvim-telescope/telescope.nvim",
      lazy = true,
      cmd = "Telescope", -- Loads when :Telescope is executed
    },

    -- 2. Load on FileType
    {
      "nvim-treesitter/nvim-treesitter",
      lazy = true,
      ft = { "lua", "python", "rust" }, -- Loads when opening these filetypes
    },

    -- 3. Load on Event with Pattern
    {
      "rust-lang/rust.vim",
      lazy = true,
      event = "BufReadPre *.rs", -- Pattern matching for autocmd events
    },

    -- 4. Load on Keymap
    {
      "folke/flash.nvim",
      lazy = true,
      keys = { "s", { "S", mode = { "n", "x" } } },
    }
  }
})
Guide

Advanced Hooks & Dependencies

Configure context-aware init and config callbacks, post-install build hooks, nested dependencies, and local dev plugins.

Advanced Hooks (examples/03_advanced_hooks.lua)
require("pack").setup({
  plugins = {
    -- Dependencies: plenary loads automatically before telescope
    {
      "nvim-telescope/telescope.nvim",
      dependencies = { "nvim-lua/plenary.nvim" },
      cmd = "Telescope",
    },

    -- Build Hooks: run 'make' after cloning/updating
    {
      "nvim-telescope/telescope-fzf-native.nvim",
      build = "make", -- can also be a Lua function(plugin)
    },

    -- Context-Aware Config & Init Hooks
    {
      "nvim-lualine/lualine.nvim",
      init = function(plugin)
        -- Runs BEFORE plugin loads (plugin.path available)
        vim.g.lualine_path = plugin.path
      end,
      config = function(plugin, opts)
        -- Runs AFTER plugin loads
        require("lualine").setup(opts)
      end
    },

    -- Conditional Loading & Priority
    {
      "folke/tokyonight.nvim",
      priority = 1000, -- Load early
      cond = function() return not vim.g.vscode end,
    },

    -- Local plugin path
    {
      "~/projects/my-local-plugin",
      config = function() require("my-local-plugin").setup() end
    }
  }
})
Guide

Modular Configuration

Split your plugin specifications cleanly across multiple files in lua/plugins/*.lua using { import = "plugins" }.

Modular Folder Import (examples/08_modular_configuration.lua)
-- 1. Main entry point (~/.config/nvim/init.lua)
vim.pack.add({ "https://github.com/igmrrf/pack.nvim" })
vim.cmd.packadd("pack.nvim")

require("pack").setup({
  plugins = {
    { "igmrrf/pack.nvim" },
    { import = "plugins" }, -- Imports all files in lua/plugins/**/*.lua
  }
})

-- 2. Modular spec file (~/.config/nvim/lua/plugins/tabscope.lua)
-- IMPORTANT: Simply return the spec table (do NOT call vim.pack.add inside)
return {
  "backdround/tabscope.nvim",
  lazy = true,
  opts = {},
}
Guide

Adopting Disk vim.pack Plugins

pack.nvim automatically recognizes plugins installed via native vim.pack.add under site/pack/core/opt without re-cloning.

Adopting Disk Plugins (examples/04_adopting_vim_pack.lua)
require("pack").setup({
  plugins = {
    -- Already cloned on disk by native vim.pack? Just declare it in spec:
    { "nvim-lua/plenary.nvim" },
  }
})
Guide

Customizing Dashboard UI & Icons

Configure floating window borders and customize dashboard status icons.

Custom UI Options (examples/05_custom_ui.lua)
require("pack").setup({
  ui = {
    -- Border styles: "single", "double", "rounded", "solid", "shadow"
    border = "double", 
    icons = {
      loaded = "🟢",
      not_loaded = "⚪",
      error = "🔴",
      sync = "🔄"
    }
  },
  plugins = { { "igmrrf/pack.nvim" } }
})
Migration Guide

Migrating from lazy.nvim

pack.nvim is intentionally designed to feel familiar to lazy.nvim users. Here is how to convert your specs.

Before & After Comparison (examples/09_migration_from_lazy.lua)
-- BEFORE: lazy.nvim
-- local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
-- require("lazy").setup({ { "folke/which-key.nvim", opts = {} } })

-- AFTER: pack.nvim
vim.g.mapleader = " "
vim.pack.add({ { src = "https://github.com/igmrrf/pack.nvim", branch = "main" } })
vim.cmd.packadd("pack.nvim")

require("pack").setup({
  plugins = {
    { "igmrrf/pack.nvim", branch = "main" },
    { "folke/which-key.nvim", opts = {} },
    { import = "plugins" },
  }
})
Migration Guide

Migrating from packer.nvim

packer.nvim used imperative use calls. In pack.nvim, features map to declarative spec tables.

Spec Key Mapping (examples/10_migration_from_packer.lua)
-- Mapping Table:
-- packer `requires` -> pack `dependencies`
-- packer `run`      -> pack `build`
-- packer `setup`    -> pack `init`

require("pack").setup({
  plugins = {
    { "igmrrf/pack.nvim" },
    {
      "nvim-telescope/telescope.nvim",
      dependencies = { "nvim-lua/plenary.nvim" }
    },
    {
      "nvim-treesitter/nvim-treesitter",
      build = ":TSUpdate"
    }
  }
})
Migration Guide

Migrating from Imperative vim.pack.add

Stop wrapping specs in imperative vim.pack.add(...) calls inside modular files; simply return table specs.

Modular Spec Conversion (examples/11_migration_imperative.lua)
-- lua/plugins/myplugin.lua
-- Do NOT call vim.pack.add or vim.cmd.packadd inside modular files!
-- Simply return the spec table:

return {
  "nvim-lua/plenary.nvim",
  {
    "nvim-telescope/telescope.nvim",
    config = function()
      require("telescope").setup({})
    end
  }
}
Key Type Description & Usage
Exhaustive Reference

All Features Plugin Specification

An exhaustive example showing every single key and option available in pack.nvim.

Full Feature Spec (examples/07_all_features_spec.lua)
require("pack").setup({
  plugins = {
    {
      -- 1. Plugin Source
      "user/mega-plugin",                   -- Bare "owner/repo" -> GitHub
      as = "mega-plugin-custom-name",       -- Custom directory alias
      
      -- 2. Loading Mechanics
      lazy = true,                          -- Defer loading until triggered
      priority = 100,                       -- Eager load order priority
      enabled = true,                       -- Hard toggle
      cond = function(plugin) return not vim.g.vscode end,
      main = "mega.core",                   -- Override main module name
      
      -- 3. Triggers
      cmd = { "MegaCommand", "MegaToggle" },
      ft = { "lua", "python" },
      event = "BufReadPre *.md",
      keys = { { "<leader>m", "<cmd>MegaToggle<cr>", desc = "Toggle" } },
      
      -- 4. Dependencies
      dependencies = { "nvim-lua/plenary.nvim" },
      
      -- 5. Hooks & Config
      init = function(plugin) vim.g.mega_dir = plugin.path end,
      opts = { theme = "dark" },
      config = function(plugin, opts) require("mega.core").setup(opts) end,
      build = "make install",
      
      -- 6. Pinning
      branch = "main",
    }
  }
})
Reference

Commands & Dashboard Keymaps

Vim user commands and interactive dashboard keyboard shortcuts.

Technical Benchmark

Comparison Matrix

Comparing pack.nvim against legacy & modern Neovim plugin managers.

Feature / Dimension 📦 pack.nvim lazy.nvim pckr.nvim paq-nvim vim-plug
Minimum Neovim 0.12+ (vim.pack) 0.8+ 0.7+ 0.5+ Vim 7.4 / Nvim 0.2+
Backend Engine Native C (vim.pack) Custom Lua engine Native packpath Native packpath Vimscript engine
Storage Location stdpath("data")/site/pack/core/opt stdpath("data")/lazy stdpath("data")/site/pack/pckr/opt stdpath("data")/site/pack/paq/opt ~/.config/nvim/plugged
Lockfile Engine Native nvim-pack-lock.json Custom lazy-lock.json Custom lockfile None None (snapshots)
Codebase Size ~2,000 lines of Lua ~20,000+ lines of Lua ~3,500 lines of Lua ~600 lines of Lua ~2,700 lines of Vimscript
Maintenance Model Feature-frozen & Stable Active development Maintenance Minimal / Stable Maintenance