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:

PluginPurpose
lazy.nvimPlugin manager
nvim-lspconfigLanguage server support
nvim-cmpCompletion engine
telescope.nvimFuzzy finding everything
treesitterBetter syntax highlighting
oil.nvimFile management as a buffer
gitsigns.nvimGit 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 termguicolors is 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

  1. Learn the motions first. f, t, %, *, ci", da{ — these are the things that make Vim fast, not the plugins.
  2. Start with one language. Configure LSP for the one language you write most. Then expand.
  3. Resist the urge to copy someone else’s full config. You won’t understand it, and you won’t maintain it.
  4. :help is 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.