My Vim Configuration Journey
I resisted Vim for years. Every time someone told me it would “change how I think about editing,” I nodded politely and went back to VS Code. Then, on a particularly slow evening, I installed Neovim, opened a file, and couldn’t figure out how to quit for five minutes.
That was eighteen months ago. I’ve been configuring it ever since.
Starting from scratch with init.lua
The modern Neovim setup uses Lua rather than Vimscript. If you’re starting fresh, init.lua is the right call. Here’s a minimal structure that actually works:
-- ~/.config/nvim/init.lua
-- Bootstrap lazy.nvim plugin manager
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not vim.loop.fs_stat(lazypath) then
vim.fn.system({
"git", "clone", "--filter=blob:none",
"https://github.com/folke/lazy.nvim.git",
"--branch=stable", lazypath,
})
end
vim.opt.rtp:prepend(lazypath)
-- Basic options
vim.opt.number = true
vim.opt.relativenumber = true
vim.opt.tabstop = 2
vim.opt.shiftwidth = 2
vim.opt.expandtab = true
vim.opt.termguicolors = true
vim.opt.cursorline = true
vim.opt.scrolloff = 8
vim.opt.signcolumn = "yes"
-- Leader key
vim.g.mapleader = " "
The single most important thing I did early on: set mapleader before loading any plugins. Getting the order wrong causes subtle keybinding issues that take forever to diagnose.
The plugins I actually kept
After six months of accumulating plugins and then brutally pruning them, here’s what survived:
| Plugin | Purpose |
|---|---|
lazy.nvim | Plugin manager |
nvim-lspconfig | Language server support |
nvim-cmp | Completion engine |
telescope.nvim | Fuzzy finding everything |
treesitter | Better syntax highlighting |
oil.nvim | File management as a buffer |
gitsigns.nvim | Git decorations in the gutter |
Everything else I tried — status line plugins, tab bar plugins, dashboard plugins — eventually got removed. The built-in statusline is fine.
LSP is the real game-changer
This is what actually makes Neovim competitive with VS Code for day-to-day work:
-- In your plugins spec
{
"neovim/nvim-lspconfig",
config = function()
local lspconfig = require("lspconfig")
-- TypeScript
lspconfig.ts_ls.setup({
on_attach = function(client, bufnr)
-- Disable formatting in favor of prettier
client.server_capabilities.documentFormattingProvider = false
end,
})
-- Lua (for configuring Neovim itself)
lspconfig.lua_ls.setup({
settings = {
Lua = {
diagnostics = { globals = { "vim" } },
},
},
})
end,
}
Getting LSP right took me two full evenings. The payoff — inline errors, go-to-definition, rename symbol — is absolutely worth it.
The color scheme problem
This site’s color scheme is based on my terminal/Neovim colors. Getting a consistent color scheme across the terminal, Neovim, and tmux is surprisingly non-trivial.
The trick is working backwards from your terminal’s 256-color palette and making sure your
termguicolorsis on. Without it, your carefully chosen hex values will get approximated to the nearest ANSI color and everything looks wrong.
My current palette leans heavily into teal and cyan for primary syntax, with muted purple for constants and pale green for strings. The red I keep strictly for errors — it’s too visually loud to use for anything else.
What I’d tell past me
- Learn the motions first.
f,t,%,*,ci",da{— these are the things that make Vim fast, not the plugins. - Start with one language. Configure LSP for the one language you write most. Then expand.
- Resist the urge to copy someone else’s full config. You won’t understand it, and you won’t maintain it.
:helpis genuinely good. Better than most documentation for any tool I’ve used.
The configuration is never finished. That’s either the best or worst thing about it, depending on the day.