Neovim: Setup, Configuration and Plugins Guide

Tested on: Neovim 0.10 · Ubuntu 26.04 LTS · Debian 12 · Arch Linux · Fedora 44 — Last updated: June 2026
Neovim is a hyperextensible, Lua-first terminal text editor with built-in LSP support, asynchronous plugin execution, and one of the most active open-source ecosystems in the editor space. Whether you're editing configs over SSH, building a full IDE-replacement for Python or Go, or just tired of reaching for the mouse, this guide gets you from zero to a productive, well-configured Neovim setup.
Prerequisites
- Basic terminal familiarity — you can navigate directories and run commands
gitinstalled (required for plugin manager bootstrap)- For LSP features: language runtimes installed (
node,python3,goas needed) - For Telescope live grep:
ripgrep— install withsudo apt install ripgrep - A terminal with true color support (most modern terminals qualify: Alacritty, Kitty, WezTerm, iTerm2, Windows Terminal)
Install Neovim
The apt version on Ubuntu/Debian is frequently outdated — the PPA or AppImage method gives you a current stable release. Arch users always get the latest.
# Ubuntu / Debian — use the PPA for a current stable build:
sudo add-apt-repository ppa:neovim-ppa/stable
sudo apt update && sudo apt install neovim -y
# Alternative: AppImage works on any x86_64 Linux distro, no PPA needed:
curl -LO https://github.com/neovim/neovim/releases/latest/download/nvim-linux-x86_64.appimage
chmod +x nvim-linux-x86_64.appimage
sudo mv nvim-linux-x86_64.appimage /usr/local/bin/nvim
# Fedora:
sudo dnf install neovim python3-neovim -y
# Arch Linux (always latest stable):
sudo pacman -S neovim
# Verify your version:
nvim --versionExpected output:
NVIM v0.10.4
Build type: Release
LuaJIT 2.1.0-beta3Set Neovim as your default editor so tools like git commit and sudoedit open it automatically:
echo 'export EDITOR=nvim' >> ~/.bashrc
echo 'export VISUAL=nvim' >> ~/.bashrc
echo 'alias vim=nvim' >> ~/.bashrc
source ~/.bashrcModes and Essential Motions
Neovim is a modal editor. Every key does something different depending on which mode you're in. This is what makes it fast — and what confuses beginners. There are four modes you'll use constantly:
- Normal mode: The default. For navigation and commands. Press
Escfrom anywhere to return here. - Insert mode: For typing text. Enter with
i(before cursor),a(after cursor),o(new line below),O(new line above). - Visual mode: For selecting text.
v= character selection,V= line selection,Ctrl+v= block selection. - Command mode: For running editor commands. Enter with
:. Examples::w(save),:q(quit),:s/old/new/g(substitute).
The critical motions to internalize before anything else:
| Key | Action |
|---|---|
h j k l | Move left / down / up / right |
w / b | Jump forward / backward by word |
0 / $ | Start / end of line |
gg / G | Top / bottom of file |
5G | Jump to line 5 |
Ctrl+d / Ctrl+u | Scroll down / up half a page |
dd / d$ | Delete whole line / delete to end of line |
yy / p | Yank (copy) line / paste |
u / Ctrl+r | Undo / redo |
ciw | Change inner word (delete word, drop into insert) |
% | Jump to matching bracket/paren |
/pattern | Search forward; n / N = next / previous match |
:w / :q / :wq / :q! | Save / quit / save+quit / force quit |
Run the built-in interactive tutorial — it takes about 30 minutes and covers everything above with practice exercises:
:TutorConfiguration Structure
Neovim reads its config from ~/.config/nvim/. The entry point is init.lua. Splitting your config into modules under lua/ keeps things maintainable as it grows.
# Create the config directory layout:
mkdir -p ~/.config/nvim/lua/plugins
# Recommended structure:
~/.config/nvim/
├── init.lua ← entry point, sources everything else
└── lua/
├── settings.lua ← vim.opt settings
├── keymaps.lua ← key bindings
└── plugins/ ← one file per plugin config (optional)
├── lsp.lua
├── telescope.lua
└── treesitter.luaYou can start with everything in a single init.lua and split it out later when it gets unwieldy. Both approaches work.
init.lua: Core Settings
This is a production-quality base config. Copy it verbatim and adjust to taste:
-- ~/.config/nvim/init.lua
-- ─── Leader key ──────────────────────────────────────────────────────────────
-- Set BEFORE loading plugins so plugin keymaps inherit it
vim.g.mapleader = " "
vim.g.maplocalleader = " "
-- ─── Editor behaviour ────────────────────────────────────────────────────────
vim.opt.number = true -- absolute line numbers
vim.opt.relativenumber = true -- relative numbers for fast jumping
vim.opt.cursorline = true -- highlight current line
vim.opt.wrap = false -- no line wrapping
vim.opt.scrolloff = 8 -- keep 8 lines visible above/below cursor
vim.opt.signcolumn = "yes" -- always show sign column (prevents layout shift)
vim.opt.colorcolumn = "100" -- visual ruler at column 100
vim.opt.termguicolors = true -- full 24-bit colour support
-- ─── Indentation ─────────────────────────────────────────────────────────────
vim.opt.expandtab = true -- spaces instead of tabs
vim.opt.shiftwidth = 4
vim.opt.tabstop = 4
vim.opt.softtabstop = 4
vim.opt.autoindent = true
vim.opt.smartindent = true
-- ─── Search ──────────────────────────────────────────────────────────────────
vim.opt.ignorecase = true -- case-insensitive search...
vim.opt.smartcase = true -- ...unless you type a capital
vim.opt.hlsearch = true
vim.opt.incsearch = true
-- ─── Files ───────────────────────────────────────────────────────────────────
vim.opt.swapfile = false
vim.opt.backup = false
vim.opt.undofile = true -- persistent undo across sessions
vim.opt.undodir = vim.fn.stdpath("data") .. "/undodir"
-- ─── Splits ──────────────────────────────────────────────────────────────────
vim.opt.splitbelow = true -- horizontal splits open below
vim.opt.splitright = true -- vertical splits open to the right
-- ─── Key mappings ────────────────────────────────────────────────────────────
local map = vim.keymap.set
map("n", "w", ":w", { desc = "Save file" })
map("n", "q", ":q", { desc = "Quit" })
map("n", "x", ":x", { desc = "Save and quit" })
map("n", "", ":nohlsearch", { desc = "Clear search highlight" })
map("n", "e", ":Explore", { desc = "Open file explorer" })
-- Better window navigation (no need for Ctrl+w prefix):
map("n", "", "h", { desc = "Move to left split" })
map("n", "", "j", { desc = "Move to lower split" })
map("n", "", "k", { desc = "Move to upper split" })
map("n", "", "l", { desc = "Move to right split" })
-- Move selected lines up/down in visual mode:
map("v", "J", ":m '>+1gv=gv", { desc = "Move selection down" })
map("v", "K", ":m '<-2gv=gv", { desc = "Move selection up" }) Plugin Manager: lazy.nvim
lazy.nvim is the standard plugin manager for Neovim. It lazy-loads plugins by default (they load only when needed), supports lock files for reproducible installs, and has a clean UI for managing updates. Bootstrap it at the top of your init.lua — it clones itself on first launch if not present:
-- Add this block AFTER the settings above, in init.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({
-- ── Colorscheme ──────────────────────────────────────────────────────
{
"catppuccin/nvim",
name = "catppuccin",
priority = 1000, -- load before everything else
config = function()
vim.cmd.colorscheme("catppuccin-mocha")
end,
},
-- ── Status line ──────────────────────────────────────────────────────
{
"nvim-lualine/lualine.nvim",
config = function()
require("lualine").setup({ options = { theme = "catppuccin" } })
end,
},
-- ── File explorer ─────────────────────────────────────────────────────
{
"nvim-tree/nvim-tree.lua",
config = function()
require("nvim-tree").setup()
vim.keymap.set("n", "t", ":NvimTreeToggle", { desc = "Toggle file tree" })
end,
},
-- ── Fuzzy finder ─────────────────────────────────────────────────────
{
"nvim-telescope/telescope.nvim",
dependencies = { "nvim-lua/plenary.nvim" },
config = function()
local t = require("telescope.builtin")
vim.keymap.set("n", "ff", t.find_files, { desc = "Find files" })
vim.keymap.set("n", "fg", t.live_grep, { desc = "Live grep" })
vim.keymap.set("n", "fb", t.buffers, { desc = "List buffers" })
vim.keymap.set("n", "fh", t.help_tags, { desc = "Search help" })
end,
},
-- ── Syntax highlighting ───────────────────────────────────────────────
{
"nvim-treesitter/nvim-treesitter",
build = ":TSUpdate",
config = function()
require("nvim-treesitter.configs").setup({
ensure_installed = { "lua", "python", "bash", "go", "javascript", "typescript", "json", "yaml", "markdown" },
highlight = { enable = true },
indent = { enable = true },
})
end,
},
-- ── LSP ──────────────────────────────────────────────────────────────
{ "neovim/nvim-lspconfig" },
{
"williamboman/mason.nvim", -- language server installer
config = function() require("mason").setup() end,
},
{
"williamboman/mason-lspconfig.nvim", -- bridges mason + lspconfig
dependencies = { "williamboman/mason.nvim", "neovim/nvim-lspconfig" },
config = function()
require("mason-lspconfig").setup({
ensure_installed = { "pyright", "lua_ls", "bashls", "gopls", "ts_ls" },
automatic
Further Reading
