Neovim: Setup, Configuration and Plugins Guide

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.

Contents
  1. Prerequisites
  2. Install Neovim
  3. Modes and Essential Motions
  4. Configuration Structure
  5. init.lua: Core Settings
  6. Plugin Manager: lazy.nvim
    1. Further Reading

Prerequisites

  • Basic terminal familiarity — you can navigate directories and run commands
  • git installed (required for plugin manager bootstrap)
  • For LSP features: language runtimes installed (node, python3, go as needed)
  • For Telescope live grep: ripgrep — install with sudo 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 --version

Expected output:

NVIM v0.10.4
Build type: Release
LuaJIT 2.1.0-beta3

Set 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 ~/.bashrc

Modes 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 Esc from 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:

KeyAction
h j k lMove left / down / up / right
w / bJump forward / backward by word
0 / $Start / end of line
gg / GTop / bottom of file
5GJump to line 5
Ctrl+d / Ctrl+uScroll down / up half a page
dd / d$Delete whole line / delete to end of line
yy / pYank (copy) line / paste
u / Ctrl+rUndo / redo
ciwChange inner word (delete word, drop into insert)
%Jump to matching bracket/paren
/patternSearch 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:

:Tutor

Configuration 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.lua

You 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


Go up

This site uses cookies for analytics and advertising (Google AdSense). By continuing to browse, you accept our use of cookies. Learn more