flowchart TD
    A[How do you use Neovim?] --> B[Basic text editing]

    A --> C[IDE/PDE experience]

    B --> D[Use programs.neovim]

    C --> E[Configuration preference?]

    E --> F["Nix manages plugins
    Lua configures nvim"]
    E --> G[Fully in Nix]

    F --> H[Use nixCats-nvim]
    G --> I["Use nvf"]

    H --> J[Starting point?]

    J --> K[Already using lazy.nvim]
    J --> L[Want maximum control]

    K --> M["templates/LazyVim"]
    L --> N["templates/example"]

    click C "https://www.youtube.com/watch?v=QMVIJhC9Veg" _blank
    click D "https://nixos.wiki/wiki/Neovim" _blank
    click H "https://github.com/BirdeeHub/nixCats-nvim" _blank
    click K "https://github.com/folke/lazy.nvim" _blank
    click I "https://github.com/NotAShelf/nvf" _blank
    click M "https://github.com/BirdeeHub/nixCats-nvim/tree/main/templates/LazyVim" _blank
    click N "https://github.com/BirdeeHub/nixCats-nvim/tree/main/templates/example" _blank

programs.neovim

If the following describes how you use neovim:

  • Mostly as an text editor (editing files here and there)
  • Do not use it for development (without regard for LSP and Treesitters)
  • Do not use or care about the plugin ecosystem (i.e., “what is mini.nvim or telescope.nvim?”)

Then programs.neovim is perfect:

programs.neovim = {
  enable = true;
  configure = {
    customRC = ''
      set number
      set wrap
      set relativenumber
      set noshowmatch
      set noshowmode
    '';
  };
};

nixCats-nvim or nvf for complete IDE experience

If you use neovim as your IDE or Personalized Development Environment (PDE), then go for nixCats-nvim or nvf.

If you want to use nix to just download and manage dependencies for you while configuring neovim in lua, then I recommend nixCats-nvim.

If you want to go all in with nix, I recommend nvf:

Lazyloading 💤? We got it! Lazyload both internal and external plugins at will.

I have not tried nvf but it’s what I would try if I didn’t try nixvim first.

Why I don’t recommend nixvim

1. Slow startup time if you have a lot of plugins

I first tried nixvim. I finished porting over all my options and plugins. Then I realized the startup time is much slower than what it used to be. Turned out I need to set up lazy loading but the its support still experimental. Regardless, I still proceeded to making plugins lazy load as much as possible and have some performance tweaks:

  performance = {
    byteCompileLua = {
      enable = true;
      nvimRuntime = true;
      configs = true;
      plugins = true;
    };
  };

but I was not satisfied with the startup time.

After migrating to nixCats-nvim, the startup time difference is very noticeable:

benchmark.png

It’s 3 times faster! I really could not bear the startup in nixvim. To be fair, it’s not nixvim’s fault. I just have a very bloated config.

2. Translating lua to nix is no fun

I’m not even talking about the straightforward translation like mapping keymaps to nix. It takes time yes, but it’s straightforward and just grunt work. I’m talking about the custom settings that needs figuring out the options in nix:

{
  autoCmd = [
    # Close specific buffers with 'q'
    {
      event = "FileType";
      pattern = [ "qf" "help" "man" "lspinfo" ];
      callback.__raw = ''
        function()
        vim.keymap.set("n", "q", ":close<CR>", { noremap = true, silent = true })
        end
      '';
    }
  ];

  # filetypes.nix (see `:h ftdetect`)
  autoGroups = { filetypes = { }; };
  files."ftdetect/snap.lua".autoCmd = [{
    group = "filetypes";
    event = [ "BufRead" "BufNewFile" ];
    pattern = [ "%.tsx%.snap$" ];
    command = "set ft=typescript";
  }];

  # ftplugin.nix (see `:h ftplugin`)
  extraFiles = {
    "ftplugin/help.lua".text = ''
      local map = require("core.utils").map
      local opts = { noremap = true, silent = true, buffer = 0 }

      map("n", "<CR>", "<C-]>", opts)
      map("n", "<BS>", "<C-T>", opts)
      map("n", "o", "/'\\l\\{2,\\}'<CR>", opts)
      map("n", "O", "?'\\l\\{2,\\}'<CR>", opts)
    '';
  };

  extraConfigLua = ''
    -- don't copy replaced text after pasting
    vim.keymap.set("x", "p", function()
      return 'pgv"' .. vim.v.register .. "y"
    end, { remap = false, expr = true })

    vim.cmd([[
    " wean off `:x` and `:q`
    cnoremap <expr> <CR> getcmdtype() == ":" && index(["q", "x"], getcmdline()) >= 0 ? "<C-u>" : "<CR>"
    ]])
  '';
}

When you have a lot of custom settings, figuring out how to translate lua to nix is a big pain.

Which template to use in nixCats-nvim?

If you’re already using lazy.nvim, I recommend templates/LazyVim.

lazy.nvim is a phenomenal plugin manager. Until I moved onto using lze, I never appreciated how much lazy.nvim did for me. It was really making plugin management very lazy.

If you want to get rid of lazy.nvim (because why use another plugin manager when we already have nix), I recommend templates/example. It uses lze, the lazy-loading library that is used in conjunction with nixCats-nvim. Do I really recommend it? Not really. Given lazy.nvim is a plugin manager and lze is a library, you have to do a lot more setup and config to achieve what lazy.nvim does for you.

Conclusion

If you decide to dive deep into nixCats-nvim, check out my How to Use nixCats-nvim in NixOS .