{
  "path": "/now/migrating-from-lazy-to-vim-pack",
  "site": "at://did:plc:ia2zdnhjaokf5lazhxrmj6eu/site.standard.publication/3mbykzswhqc2x",
  "$type": "site.standard.document",
  "title": "Migrating from lazy.nvim to vim.pack",
  "content": {
    "$type": "site.standard.content.markdown",
    "markdown": "Like many others who are upgrading to Neovim 0.12.0, I decided to give vim.pack a try by following the [excellent guide](https://echasnovski.com/blog/2026-03-13-a-guide-to-vim-pack) by Evgeni Chasnovski. I did encounter a few bumps that I figured I would document here just in case others find themselves stuck. \n\n- Symlinks - Bit more of a dummy move on my part, but I wasn't paying attention to my symlink setup that maps my git controlled repo `~/dotfiles/nvim` to `~/.config/nvim` and ended up missing some new folders I created. Re-linking helped fixed this, so if you have this kind of setup keep in mind to do this! \n\n- Multiple Folder Organization - Something I wanted to do originally is replicate the setup I had with lazy where each plugin was in it's own file. This is possible vim.pack, but it requires using a `plugin` directory that is adjacent to your `init.lua` file. I have a slightly different structure where this style didn't quite fit in, so I stuck with a single file setup instead. \n\n- Lazy Loading - The guide linked at the beginning has some great tips for how you can lazy load plugins based on different events, so I would highly recommend looking at which plugins don't need to be loaded immediately or only need to be loaded while in insert mode. I was able to shave an extra 10ms off my startup time! \n\nOverall this resulted in a lean one file config for plugins that is only 117 lines long and has an average startup time of 35ms.\n\n```lua\n-- ============================================================================\n-- Colorscheme (must load at startup)\n-- ============================================================================\nvim.pack.add({\n\t\"https://github.com/stevedylandev/compline-nvim\",\n  'https://github.com/echasnovski/mini.nvim',\n})\nvim.cmd.colorscheme('compline')\n\n-- ============================================================================\n-- Mini.nvim — startup modules\n-- ============================================================================\n\nlocal win_config = function()\n  local height = math.floor(0.618 * vim.o.lines)\n  local width = math.floor(0.618 * vim.o.columns)\n  return {\n    anchor = 'NW',\n    height = height,\n    width = width,\n    row = math.floor(0.5 * (vim.o.lines - height)),\n    col = math.floor(0.5 * (vim.o.columns - width)),\n  }\nend\n\nrequire(\"mini.pick\").setup({\n  mappings = {\n    choose_marked = '<C-y>',\n    move_down     = '<C-j>',\n    move_up       = '<C-k>',\n  },\n  window = { config = win_config }\n})\n\nvim.api.nvim_set_hl(0, \"MiniPickMatchCurrent\",\n  { bg = vim.g.terminal_color_8\n  })\n\nrequire('mini.icons').setup()\nvim.api.nvim_set_hl(0, 'MiniIconsAzure', { fg = vim.g.terminal_color_12 })\nvim.api.nvim_set_hl(0, 'MiniIconsBlue', { fg = vim.g.terminal_color_4 })\nvim.api.nvim_set_hl(0, 'MiniIconsCyan', { fg = vim.g.terminal_color_6 })\nvim.api.nvim_set_hl(0, 'MiniIconsGreen', { fg = vim.g.terminal_color_2 })\nvim.api.nvim_set_hl(0, 'MiniIconsGrey', { fg = vim.g.terminal_color_8 })\nvim.api.nvim_set_hl(0, 'MiniIconsOrange', { fg = vim.g.terminal_color_3 })\nvim.api.nvim_set_hl(0, 'MiniIconsPurple', { fg = vim.g.terminal_color_5 })\nvim.api.nvim_set_hl(0, 'MiniIconsRed', { fg = vim.g.terminal_color_1 })\nvim.api.nvim_set_hl(0, 'MiniIconsYellow', { fg = vim.g.terminal_color_11 })\n\nrequire('mini.diff').setup({\n  view = {\n    style = vim.go.number and 'sign' or 'number',\n\n    signs = {\n      add = \"+\",\n      change = \"~\",\n      delete = \"-\",\n      topdelete = \"\",\n      changedelete = \"▎\",\n      untracked = \"+\"\n    },\n\n    priority = 199,\n  },\n})\nrequire('mini.statusline').setup()\nrequire('mini.extra').setup()\n\n-- ============================================================================\n-- Deferred — loads right after startup via vim.schedule()\n-- ============================================================================\nvim.schedule(function()\n  vim.pack.add({\n    \"https://github.com/christoomey/vim-tmux-navigator\",\n  })\n\n  require(\"mini.comment\").setup({\n    mappings = {\n      comment = 'gb',\n      comment_visual = 'gb',\n      textobject = 'gb'\n    }\n  })\n\n  require('mini.surround').setup({\n    mappings = {\n      replace = 'cs', -- Replace surrounding\n    },\n  })\n\n  require('mini.files').setup({\n    mappings = {\n      close      = '<ESC>',\n      go_in_plus = '<CR>'\n    }\n  })\nend)\n\n-- ============================================================================\n-- Lazy — loads on InsertEnter\n-- ============================================================================\nvim.api.nvim_create_autocmd('InsertEnter', { once = true, callback = function()\n  require(\"mini.completion\").setup({\n    mappings = {\n      scroll_down = '<C-j>',\n      scroll_up = '<C-k>',\n    },\n  })\n\n  local gen_loader = require('mini.snippets').gen_loader\n  require('mini.snippets').setup({\n    snippets = {\n      gen_loader.from_lang(),\n    },\n  })\n  MiniSnippets.start_lsp_server()\nend })\n```\n\nAll of my dotfiles can be found at [github.com/stevedylandev/dotfiles](https://github.com/stevedylandev/dotfiles)!"
  },
  "coverImage": {
    "$type": "blob",
    "ref": {
      "$link": "bafkreibuxyp2gth3igqik7fxu4cm4nducetgp67hhlx36bwahgnuw4xmoa"
    },
    "mimeType": "image/png",
    "size": 2522
  },
  "publishedAt": "2026-03-31T15:21:11.642Z",
  "textContent": "Like many others who are upgrading to Neovim 0.12.0, I decided to give vim.pack a try by following the excellent guide by Evgeni Chasnovski. I did encounter a few bumps that I figured I would document here just in case others find themselves stuck. \n\n- Symlinks - Bit more of a dummy move on my part, but I wasn't paying attention to my symlink setup that maps my git controlled repo ~/dotfiles/nvim to ~/.config/nvim and ended up missing some new folders I created. Re-linking helped fixed this, so if you have this kind of setup keep in mind to do this! \n\n- Multiple Folder Organization - Something I wanted to do originally is replicate the setup I had with lazy where each plugin was in it's own file. This is possible vim.pack, but it requires using a plugin directory that is adjacent to your init.lua file. I have a slightly different structure where this style didn't quite fit in, so I stuck with a single file setup instead. \n\n- Lazy Loading - The guide linked at the beginning has some great tips for how you can lazy load plugins based on different events, so I would highly recommend looking at which plugins don't need to be loaded immediately or only need to be loaded while in insert mode. I was able to shave an extra 10ms off my startup time! \n\nOverall this resulted in a lean one file config for plugins that is only 117 lines long and has an average startup time of 35ms.\n\n``lua\n-- ============================================================================\n-- Colorscheme (must load at startup)\n-- ============================================================================\nvim.pack.add({\n\t\"https://github.com/stevedylandev/compline-nvim\",\n  'https://github.com/echasnovski/mini.nvim',\n})\nvim.cmd.colorscheme('compline')\n\n-- ============================================================================\n-- Mini.nvim — startup modules\n-- ============================================================================\n\nlocal win_config = function()\n  local height = math.floor(0.618  vim.o.lines)\n  local width = math.floor(0.618  vim.o.columns)\n  return {\n    anchor = 'NW',\n    height = height,\n    width = width,\n    row = math.floor(0.5  (vim.o.lines - height)),\n    col = math.floor(0.5  (vim.o.columns - width)),\n  }\nend\n\nrequire(\"mini.pick\").setup({\n  mappings = {\n    choose_marked = '<C-y>',\n    move_down     = '<C-j>',\n    move_up       = '<C-k>',\n  },\n  window = { config = win_config }\n})\n\nvim.api.nvim_set_hl(0, \"MiniPickMatchCurrent\",\n  { bg = vim.g.terminal_color_8\n  })\n\nrequire('mini.icons').setup()\nvim.api.nvim_set_hl(0, 'MiniIconsAzure', { fg = vim.g.terminal_color_12 })\nvim.api.nvim_set_hl(0, 'MiniIconsBlue', { fg = vim.g.terminal_color_4 })\nvim.api.nvim_set_hl(0, 'MiniIconsCyan', { fg = vim.g.terminal_color_6 })\nvim.api.nvim_set_hl(0, 'MiniIconsGreen', { fg = vim.g.terminal_color_2 })\nvim.api.nvim_set_hl(0, 'MiniIconsGrey', { fg = vim.g.terminal_color_8 })\nvim.api.nvim_set_hl(0, 'MiniIconsOrange', { fg = vim.g.terminal_color_3 })\nvim.api.nvim_set_hl(0, 'MiniIconsPurple', { fg = vim.g.terminal_color_5 })\nvim.api.nvim_set_hl(0, 'MiniIconsRed', { fg = vim.g.terminal_color_1 })\nvim.api.nvim_set_hl(0, 'MiniIconsYellow', { fg = vim.g.terminal_color_11 })\n\nrequire('mini.diff').setup({\n  view = {\n    style = vim.go.number and 'sign' or 'number',\n\n    signs = {\n      add = \"+\",\n      change = \"~\",\n      delete = \"-\",\n      topdelete = \"\",\n      changedelete = \"▎\",\n      untracked = \"+\"\n    },\n\n    priority = 199,\n  },\n})\nrequire('mini.statusline').setup()\nrequire('mini.extra').setup()\n\n-- ============================================================================\n-- Deferred — loads right after startup via vim.schedule()\n-- ============================================================================\nvim.schedule(function()\n  vim.pack.add({\n    \"https://github.com/christoomey/vim-tmux-navigator\",\n  })\n\n  require(\"mini.comment\").setup({\n    mappings = {\n      comment = 'gb',\n      comment_visual = 'gb',\n      textobject = 'gb'\n    }\n  })\n\n  require('mini.surround').setup({\n    mappings = {\n      replace = 'cs', -- Replace surrounding\n    },\n  })\n\n  require('mini.files').setup({\n    mappings = {\n      close      = '<ESC>',\n      go_in_plus = '<CR>'\n    }\n  })\nend)\n\n-- ============================================================================\n-- Lazy — loads on InsertEnter\n-- ============================================================================\nvim.api.nvim_create_autocmd('InsertEnter', { once = true, callback = function()\n  require(\"mini.completion\").setup({\n    mappings = {\n      scroll_down = '<C-j>',\n      scroll_up = '<C-k>',\n    },\n  })\n\n  local gen_loader = require('mini.snippets').gen_loader\n  require('mini.snippets').setup({\n    snippets = {\n      gen_loader.from_lang(),\n    },\n  })\n  MiniSnippets.start_lsp_server()\nend })\n``\n\nAll of my dotfiles can be found at github.com/stevedylandev/dotfiles!"
}