This skill should be used when the user asks about "neovim config", "nvim setup", "vim best practices", "neovim patterns", "modern neovim", "lua configuration", "neovim 0.10", or wants guidance on configuring Neovim following current standards and conventions.
Modern Neovim configuration follows specific patterns that leverage Lua, built-in LSP, and the plugin ecosystem effectively. This skill provides guidance on structuring configurations, choosing plugins, and following current best practices.
Prefer Lua for all configuration. Neovim 0.5+ has first-class Lua support with better performance and cleaner syntax than Vimscript.
Structure:
~/.config/nvim/
├── init.lua # Entry point
└── lua/
└── <username>/ # Namespace your config
├── init.lua
├── options.lua
├── keymaps.lua
├── autocmds.lua
└── lazy.lua # Plugin manager setup
Entry point pattern:
-- init.lua
require("<username>")
Module organization:
-- lua/<username>/init.lua
require("<username>.options")
require("<username>.keymaps")
require("<username>.autocmds")
require("<username>.lazy")
Use lazy.nvim as the modern plugin manager. It provides lazy-loading, lockfile management, and excellent performance.
Setup pattern:
-- lua/<username>/lazy.lua
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)
require("lazy").setup("plugins", {
-- Configuration options
})
Plugin specs:
Create individual plugin files in lua/plugins/:
-- lua/plugins/telescope.lua
return {
"nvim-telescope/telescope.nvim",
cmd = "Telescope", -- Lazy-load on command
keys = { -- Lazy-load on keymap
{ "<leader>ff", "<cmd>Telescope find_files<cr>" },
},
dependencies = { "nvim-lua/plenary.nvim" },
config = function()
require("telescope").setup({
-- Configuration
})
end,
}
Options:
-- lua/<username>/options.lua
local opt = vim.opt
opt.number = true
opt.relativenumber = true
opt.expandtab = true
opt.shiftwidth = 2
opt.tabstop = 2
-- Use opt for vim options, not vim.o/vim.wo/vim.bo directly
Keymaps:
-- lua/<username>/keymaps.lua
local keymap = vim.keymap.set
-- Set leader key first
vim.g.mapleader = " "
vim.g.maplocalleader = " "
-- Use descriptive opts
keymap("n", "<leader>ff", "<cmd>Telescope find_files<cr>", { desc = "Find files" })
keymap("n", "<leader>fg", "<cmd>Telescope live_grep<cr>", { desc = "Live grep" })
-- Prefer lua functions over vim commands when possible
keymap("n", "<Esc>", function() vim.cmd("nohlsearch") end, { desc = "Clear highlights" })
Autocommands:
-- lua/<username>/autocmds.lua
local autocmd = vim.api.nvim_create_autocmd
local augroup = vim.api.nvim_create_augroup
-- Create augroup for organization
local highlight_group = augroup("YankHighlight", { clear = true })
autocmd("TextYankPost", {
group = highlight_group,
pattern = "*",
callback = function()
vim.highlight.on_yank({ higroup = "IncSearch", timeout = 200 })
end,
desc = "Highlight yanked text",
})
Separate concerns into focused files:
Core configuration files:
init.lua - Entry point onlyoptions.lua - Vim options and settingskeymaps.lua - Key mappingsautocmds.lua - Autocommands and event handlerslazy.lua - Plugin manager setupPlugin files:
Create one file per plugin or logical group in lua/plugins/:
colorscheme.lua - Color schemelsp.lua - LSP configurationtreesitter.lua - Treesittertelescope.lua - Fuzzy findergitsigns.lua - Git integrationUtility modules:
Optional lua/<username>/util/ for shared functions:
util/init.lua - Common utilitiesutil/lsp.lua - LSP helpersutil/keymaps.lua - Keymap utilitiesAlways namespace under your username to avoid conflicts:
-- lua/kriscard/init.lua
require("kriscard.options")
require("kriscard.keymaps")
This prevents collisions with plugin code and makes the config portable.
Neovim has built-in LSP client. Use nvim-lspconfig for server configurations.
Recommended setup:
-- lua/plugins/lsp.lua
return {
"neovim/nvim-lspconfig",
dependencies = {
"williamboman/mason.nvim",
"williamboman/mason-lspconfig.nvim",
},
config = function()
require("mason").setup()
require("mason-lspconfig").setup({
ensure_installed = { "lua_ls", "tsserver", "pyright" },
})
local lspconfig = require("lspconfig")
local capabilities = require("cmp_nvim_lsp").default_capabilities()
-- Configure each server
lspconfig.lua_ls.setup({ capabilities = capabilities })
lspconfig.tsserver.setup({ capabilities = capabilities })
end,
}
On-attach pattern:
local on_attach = function(client, bufnr)
local opts = { buffer = bufnr }
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>ca", vim.lsp.buf.code_action, opts)
end
lspconfig.lua_ls.setup({
capabilities = capabilities,
on_attach = on_attach,
})
Plugin manager:
LSP & Completion:
Syntax & UI:
Git:
Quality of life:
Evaluate plugins based on:
Prefer plugins that:
Avoid plugins that:
Load plugins only when needed:
By command:
{ "plugin/name", cmd = "CommandName" }
By keymap:
{ "plugin/name", keys = "<leader>x" }
By filetype:
{ "plugin/name", ft = { "python", "lua" } }
By event:
{ "plugin/name", event = "BufReadPost" }
Defer loading:
{
"plugin/name",
lazy = true, -- Don't load automatically
dependencies = { "other/plugin" },
}
Measure startup time:
nvim --startuptime startup.log
Common optimizations:
lazy = true for plugins loaded by othersevent = "VeryLazy":Lazy profileSet leader early in init.lua:
vim.g.mapleader = " "
vim.g.maplocalleader = " "
Organize by prefix:
<leader>f - Find/search (telescope)<leader>g - Git operations<leader>b - Buffer management<leader>w - Window management<leader>l - LSP operations<leader>t - Terminal/testsUse descriptive options:
keymap("n", "<leader>ff", "<cmd>Telescope find_files<cr>", {
desc = "Find files",
silent = true,
})
Prefer Lua functions:
keymap("n", "<leader>w", function()
vim.cmd.write()
print("File saved")
end, { desc = "Save file" })
Use which-key for discoverability:
require("which-key").add({
{ "<leader>f", group = "Find" },
{ "<leader>ff", desc = "Find files" },
{ "<leader>fg", desc = "Live grep" },
})
-- Bad
vim.cmd("set number")
-- Good
vim.opt.number = true
-- Bad
vim.api.nvim_set_keymap("n", "<leader>ff", "<cmd>Telescope find_files<cr>", {})
-- Good
vim.keymap.set("n", "<leader>ff", "<cmd>Telescope find_files<cr>", {
desc = "Find files",
silent = true,
})
-- Bad - loads everything at startup
return { "plugin/name" }
-- Good - loads when needed
return {
"plugin/name",
cmd = "PluginCommand",
keys = "<leader>x",
}
-- Bad
require("kriscard.config")
-- Good - use stdpath
local config_path = vim.fn.stdpath("config") .. "/lua/kriscard"
Take advantage of modern features:
Built-in commenting:
vim.keymap.set("n", "gcc", "gcc", { remap = true })
-- Can replace Comment.nvim in 0.10+
Improved diagnostics:
vim.diagnostic.config({
virtual_text = true,
signs = true,
float = { border = "rounded" },
})
Inlay hints:
vim.lsp.inlay_hint.enable(true)
Avoid deprecated APIs:
Old autocommand syntax:
-- Bad (deprecated)
vim.cmd([[
augroup MyGroup
autocmd!
autocmd BufWritePre * echo "Saving..."
augroup END
]])
-- Good
local autocmd = vim.api.nvim_create_autocmd
autocmd("BufWritePre", {
pattern = "*",
callback = function()
print("Saving...")
end,
})
For detailed information, consult:
references/plugin-recommendations.md - Curated plugin list by categoryreferences/lsp-setup.md - Comprehensive LSP configuration guidereferences/migration-guide.md - Migrating from Vimscript to LuaWorking examples in examples/:
init.lua - Complete minimal configlazy-spec-example.lua - Plugin specification patternsFile structure checklist:
init.lua exists at config rootlua/lua/plugins/ directoryCode quality checklist:
vim.opt for optionsvim.keymap.set for keymapsvim.api.nvim_create_autocmd for autocommandsPerformance checklist:
--startuptime)event = "VeryLazy"Follow these patterns for maintainable, performant, and modern Neovim configurations.