Contents

Neovim in 2026: A Practical Setup for People Who Also Have Work to Do

A modern config that gets you to a working editor without losing a weekend

Contents

Neovim has a reputation, and it isn’t entirely undeserved: the editor you spend a weekend configuring instead of using. I’ve lost those weekends. I’ve stayed up until 2am chasing a plugin conflict that turned out to be a load-order problem, and I’ve rebuilt my config from scratch more times than I’ll admit in writing. I’ve also come out the other side with an editor I genuinely prefer to anything else — and the good news, the whole reason for this post, is that in 2026 you no longer have to pay that tax. The ecosystem has matured into a small set of plugins that do the heavy lifting, and a sensible config is now a few hundred lines of readable Lua rather than a haunted 3,000-line init.vim inherited from a stranger’s dotfiles. This is the setup I’d hand someone who wants a capable editor and also, crucially, has actual work to do this week.

Why bother, before we get to how

Advertisement

Let me answer the obvious objection first, because “why not just use VS Code” is a fair question and I explain “why” before “how” on principle. Three reasons keep me here. The editor lives in the terminal, so it works identically on my laptop and over SSH on a headless box, with no remote-desktop lag and no “install the GUI on the server” nonsense. It’s fast — startup is measured in tens of milliseconds and it never chews a gigabyte of RAM to hold a text file. And it’s yours: every behaviour is a line of Lua you can read, change, and understand, which matters enormously the first time something breaks at an inconvenient moment. If none of those land for you, that’s genuinely fine, and I’ll come back to who shouldn’t bother at the end. If even one does, read on.

The four pieces that matter

Modern Neovim stands on four pillars, and understanding what each is for saves a lot of confusion later when you’re reading someone else’s config and can’t tell the essential from the decorative.

  • lazy.nvim — the plugin manager. It installs plugins and “lazy-loads” them, spinning each up only when it’s actually needed, which is what keeps startup fast even with fifty plugins declared.
  • Treesitter — a real parser for your code. It gives you syntax highlighting that understands structure rather than guessing with regexes, plus smarter indentation and structural text selection.
  • LSP — the Language Server Protocol. The same engine that powers IntelliSense elsewhere: completion, go-to-definition, rename, live diagnostics. Neovim ships a built-in LSP client; you just point it at language servers.
  • Telescope — a fuzzy finder for files, text, symbols, and just about everything else. Once it’s in your fingers you’ll wonder how you ever navigated a project without it.

You can absolutely start from a pre-built distribution like LazyVim or kickstart.nvim, and for a lot of people that’s the right call — kickstart in particular is a single well-commented file designed to be read. But rolling a small config yourself means you understand every line, and that understanding is exactly what pays off the first time a plugin update breaks something and you need to know where to look.

Bootstrapping lazy.nvim

Advertisement

Everything lives under ~/.config/nvim/, with init.lua as the entry point. The standard pattern is to self-install lazy.nvim if it’s missing, then hand it a list of plugin specs.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
-- ~/.config/nvim/init.lua
vim.g.mapleader = " "                 -- space as leader, set before plugins load

local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not vim.uv.fs_stat(lazypath) then  -- vim.uv, not the deprecated vim.loop
  vim.fn.system({
    "git", "clone", "--filter=blob:none",
    "https://github.com/folke/lazy.nvim.git",
    "--branch=stable", lazypath,
  })
end
vim.opt.rtp:prepend(lazypath)

require("lazy").setup("plugins")      -- loads every file under lua/plugins/

Two details worth knowing. Setting mapleader before plugins load matters, because plugins bind their keymaps relative to whatever the leader is at load time — set it late and half your shortcuts land on the wrong key. And vim.uv is the current name for the libuv bindings; the old vim.loop still works but is deprecated and throwing warnings, so use vim.uv in anything you write today. That last setup("plugins") line tells lazy.nvim to read every file under lua/plugins/, so you can split your config into one small file per concern rather than one monster. Launch Neovim, lazy.nvim installs itself and everything you’ve specified, and :Lazy gives you a tidy dashboard to manage, update, and profile it all.

Treesitter and LSP, the core of the experience

Two short specs get you the bulk of a modern editing experience. Treesitter first:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
-- lua/plugins/treesitter.lua
return {
  "nvim-treesitter/nvim-treesitter",
  build = ":TSUpdate",
  config = function()
    require("nvim-treesitter.configs").setup({
      ensure_installed = { "lua", "go", "python", "bash", "markdown" },
      highlight = { enable = true },
      indent = { enable = true },
    })
  end,
}

For LSP, the painless route in 2026 pairs mason.nvim, which installs language servers for you, with nvim-lspconfig, which knows how to wire each one up. You no longer hand-install gopls or pyright system-wide and fight your PATH — Mason drops them into Neovim’s own data directory where nothing else can collide with them.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
-- lua/plugins/lsp.lua
return {
  "neovim/nvim-lspconfig",
  dependencies = {
    "mason-org/mason.nvim",
    "mason-org/mason-lspconfig.nvim",
  },
  config = function()
    require("mason").setup()
    require("mason-lspconfig").setup({
      ensure_installed = { "lua_ls", "gopls", "pyright" },
    })

    vim.api.nvim_create_autocmd("LspAttach", {
      callback = function(args)
        local opts = { buffer = args.buf }
        vim.keymap.set("n", "gd", vim.lsp.buf.definition, opts)
        vim.keymap.set("n", "K", vim.lsp.buf.hover, opts)
        vim.keymap.set("n", "<leader>rn", vim.lsp.buf.rename, opts)
        vim.keymap.set("n", "<leader>ca", vim.lsp.buf.code_action, opts)
      end,
    })
  end,
}

One currency note, because this bit the whole community recently: Mason and mason-lspconfig moved to the mason-org/ GitHub org and shipped a 2.0 that assumes Neovim 0.11’s native vim.lsp.config() mechanism and drops the old setup_handlers(). The spec above works on current versions; if you’re pinned to Neovim 0.10 or earlier, stay on the v1.x branches of both plugins or you’ll hit confusing breakage. The LspAttach autocommand wires keymaps only in buffers where a language server actually attached, so gd (go to definition), K (hover docs), and rename exist exactly where they make sense and nowhere else. Run :Mason to browse and install more servers from a menu.

Telescope, the navigation you’ll use constantly

Telescope is the plugin that changes how you move. Fuzzy-find files, grep the whole project live, jump to any symbol — all from a few leader keys.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
-- lua/plugins/telescope.lua
return {
  "nvim-telescope/telescope.nvim",
  dependencies = { "nvim-lua/plenary.nvim" },
  config = function()
    local builtin = require("telescope.builtin")
    vim.keymap.set("n", "<leader>ff", builtin.find_files, { desc = "Find files" })
    vim.keymap.set("n", "<leader>fg", builtin.live_grep,  { desc = "Live grep" })
    vim.keymap.set("n", "<leader>fb", builtin.buffers,    { desc = "Buffers" })
  end,
}

Install ripgrep on your system and live_grep becomes blisteringly fast across huge codebases — it’s shelling out to rg, so you get its speed for free. With space-f-g I search every file in a repository as I type, and that single capability justifies the whole setup for me. Add a completion engine (blink.cmp is the fast newcomer; nvim-cmp the established choice) and a statusline like lualine, and you have an editor that holds its own against any IDE for a fraction of the resource use.

A word on structure and discipline

The thing that keeps a Neovim config maintainable over years — and mine has survived several — is treating it like the small software project it actually is, not a scratchpad. One file per plugin under lua/plugins/, each returning a spec, means that when a plugin misbehaves you know exactly which file to open, and you can disable it by adding enabled = false to its spec rather than commenting out a tangled block and hoping you got the braces right. Keep your own settings (line numbers, tab width, search behaviour) in a separate lua/options.lua that init.lua requires early, and keep global keymaps that don’t belong to any one plugin in lua/keymaps.lua. That separation sounds fussy for a text editor config, but it’s the difference between a setup you can revisit after six months away and one you’re afraid to touch.

Version-control the whole ~/.config/nvim/ directory in git from day one. This matters more than it sounds: it lets you experiment fearlessly, because a bad afternoon is one git checkout away from being undone, and it lets you replicate your exact editor onto a new machine or a fresh server with a single clone. lazy.nvim writes a lazy-lock.json that pins every plugin to a specific commit, and committing that lock file is what makes “the same editor everywhere” a real guarantee rather than an aspiration — the same reproducibility instinct that drives so much else in a well-run setup. When you do want to update, :Lazy update bumps everything and rewrites the lock; if an update breaks something, the lock file tells you precisely what changed and :Lazy restore rolls it back.

When it breaks — and it will, once

A few things reliably trip people up, so here’s where to look before you rage-quit to a GUI:

  • A plugin fails to load and the error is unreadable. Run :checkhealth first — it diagnoses missing dependencies (a missing git, node, or ripgrep is a common one) far faster than reading stack traces. Then :Lazy to see which plugin actually errored.
  • LSP does nothing. Open a file of the right type and run :LspInfo. If no client attached, the server probably isn’t installed (:Mason) or the filetype isn’t what you think. :Mason also shows install failures, which are usually a missing compiler or an absent node.
  • Highlighting looks wrong or plain. :TSInstall <language> for the parser, and check :checkhealth nvim-treesitter. A missing C compiler stops Treesitter building parsers, and the symptom is just “no nice highlighting” with no obvious error.
  • A config edit seems to do nothing. Neovim reads config at startup; restart it, or you’re testing the old state. Obvious, and yet.

If you like this kind of terminal-first, own-every-line tooling, it’s of a piece with running your own local LLMs instead of an API subscription or a node-based ComfyUI setup for image generation — same instinct to keep the tools close and under your hand.

The honest verdict

Is Neovim worth it in 2026? It depends entirely on who you are, and I’d rather steer you right than win a convert. If you bounce between languages, live in the terminal, work over SSH on remote boxes, or simply value a tool you can shape exactly to your hands, the answer is an enthusiastic yes — and the modern stack means you reach “productive” in an evening, not a lost weekend. The config above is genuinely most of the way there; add a colourscheme and a completion engine and you’re done.

But be honest with yourself. If you want an editor that’s brilliant five minutes after install and you never again want to open a config file, a mainstream graphical IDE is the saner choice, and there is no shame whatsoever in it — the best editor is the one that gets out of your way. Neovim rewards tinkering and quietly punishes those who’d rather not. I’m firmly in the tinkering camp, so it’s my daily driver — but I’d only push it on you if “I get to configure my own editor” reads as a feature rather than a chore.

Advertisement
Advertisement
Smarc
Written by Smarc

Founder and editor of vo.rs. A lifelong tinkerer who self-hosts far more than is sensible, hardens Linux boxes for fun, and prods the latest AI tools to see what they can really do. The how-to guides here are the notes Smarc wishes had existed the first time round.