TLDR

  1. Create directory ~/.local/share/nvim/site/pack/dev/opt/
  2. Clone the plugin into that dir: ~/.local/share/nvim/site/pack/dev/opt/my-plugin
  3. Add this to your Neovim config: vim.cmd.packadd "my-plugin"

With Neovim 0.12 came vim.pack, and I started using it straight away. I much prefer built-in and minimalist if it's good enough, and I can say that I haven't missed anything from Lazy.nvim so far. In fact, I much prefer how much simpler my config has resulted after the migration.

Echasnovski's guide to vim.pack was extremely helpful, and remains the best source of information on how to use vim.pack, as there doesn't seem to be any official guides on this. Here are a few of my findings:

Neovim expects plugins to be installed in ~/.local/share/nvim/site/pack. This is where Lazy was installing them, and it's where vim.pack installs them. The folder structure looks something like this:

pack
├── bundle-a
│   ├── start
   │   ├── plugin-a
│   └── opt
       ├── plugin-b
       └── plugin-c
└── bundle-b
    └── opt
        └── plugin-d

The first directory after pack/ is where the bundle goes. A bundle is a collection of plugins. When you install a plugin with vim.pack, it puts it into a bundle called core. You might already see a core bundle in that path, with all your plugins inside opt/.

The second level is either start/ or opt/. Plugins in start/ are always available after startup. Plugins in opt/ are only loaded after you call :packadd (but vim.pack does this for us). In fact vim.pack.add() checks that the plugin is available (e.g. ensure it was cloned) and calls :packadd.

Setup for local plugin development

The best and "most correct" way is to create a new bundle, which you can name however you'd like. I called mine dev. So I have:

pack
├── core
│   └── opt
       ├── other-plugin
       └── ...
└── dev
    └── opt
        └── my-local-plugin

Echasnovski's guide says you can also put it in ~/.config/nvim/pack. Neovim will pick that up, but conceptually it feels more wrong to me. $XDG_CONFIG_DIRS (~/.config) is for application configuration files, not really for artifacts or large blobs of source code. It clicks better for me if all the plugin code is in the same place. All it takes is remembering where Neovim expects it.

Finally, you must use vim.cmd.packadd() in your Neovim config so that your plugin is loaded. Note that if you are replacing an existing remote plugin (e.g. forked it to develop a new feature), you will have to comment out the vim.pack.add() command and uninstall it!

In your Neovim config:

-- vim.pack.add { "https://github.com/kikefdezl/my-plugin.nvim" }
vim.cmd.packadd "my-plugin.nvim"

For reference, here's my Neovim config.