Neovim vs Vim — Key Differences
If you have Vim experience, this lesson clarifies what changed in Neovim. If you are starting fresh, it explains what to expect when reading Vim documentation — and what applies vs what is Neovim-specific.
Core Idea
Every Vim command works in Neovim (for practical purposes). Neovim adds new capabilities — Lua config, built-in LSP, Treesitter — it does not remove Vim behavior.
Side-by-Side Comparison
| Feature | Vim | Neovim |
|---|---|---|
| Config language | Vimscript (.vimrc) | Lua (init.lua) or Vimscript (init.vim) |
| Plugin ecosystem | Vimscript / old-style | Modern Lua-native plugins |
| LSP support | Via third-party plugins (coc.nvim) | Built-in vim.lsp API |
| Treesitter | Via plugin, limited | Built-in, first-class |
| Async jobs | Via channels, complex | Native vim.loop (libuv) |
| RPC API | Basic | Full Msgpack RPC |
| Terminal emulator | :terminal (limited) | :terminal (improved) |
| UI multiplexing | No | Multiple UIs can connect |
| Popup menus | Limited | Full nvim_open_win API |
| Float windows | No | Yes (floating windows) |
| Lua version | — | LuaJIT 2.1 |
| Default config path | ~/.vimrc | ~/.config/nvim/init.lua |
| Plugin managers | Vundle, Plug | lazy.nvim, packer.nvim |
Configuration: Vimscript vs Lua
Vim uses Vimscript for configuration. Neovim supports both but Lua is preferred:
Vim (.vimrc)
set number
set tabstop=4
set expandtab
nnoremap <leader>f :Files<CR>
function! MyFunction()
echo "Hello"
endfunction
Neovim (init.lua)
vim.opt.number = true
vim.opt.tabstop = 4
vim.opt.expandtab = true
vim.keymap.set('n', '<leader>f', '<cmd>Telescope find_files<cr>')
local function my_function()
print("Hello")
end
tip
Lua is a real programming language with proper data structures, functions, and modules. Vimscript was designed exclusively for Vim and is harder to reuse and test.
The Lua API
Neovim exposes all of its functionality to Lua through the vim global namespace:
-- Options
vim.opt.number = true -- equivalent to :set number
vim.bo.filetype -- buffer-local options
vim.wo.wrap -- window-local options
-- Key mappings
vim.keymap.set(mode, lhs, rhs, opts)
-- Commands
vim.cmd("colorscheme habamax")
-- API calls
vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
-- Autocommands
vim.api.nvim_create_autocmd("BufWritePre", { ... })
-- User commands
vim.api.nvim_create_user_command("MyCmd", function() ... end, {})
Plugin Difference: Old vs New Ecosystem
Old (Vim) plugin format
" Installed via vim-plug
" Configured with Vimscript
let g:airline_theme = 'dark'
New (Neovim) plugin format
-- Installed via lazy.nvim
-- Configured as Lua tables
{
"nvim-lualine/lualine.nvim",
opts = {
options = {
theme = "auto",
},
},
}
Built-in LSP vs coc.nvim
| Approach | Vim / old Neovim | Modern Neovim |
|---|---|---|
| LSP provider | coc.nvim (full Node.js runtime) | nvim-lspconfig (built-in Neovim LSP) |
| Memory overhead | High (Node + extensions) | Low (native) |
| Config style | JSON (coc-settings.json) | Lua |
| Speed | Slower | Faster |
When You Read Vim Documentation
When you find Vim documentation or StackOverflow answers:
| Vim syntax | Neovim equivalent |
|---|---|
:set option=value | vim.opt.option = value |
:let g:var = value | vim.g.var = value |
:map <leader>x :cmd<CR> | vim.keymap.set('n', '<leader>x', ':cmd<CR>') |
autocmd Event * cmd | vim.api.nvim_create_autocmd("Event", { ... }) |
function! Foo() | local function foo() (Lua) |
Files You Will Create in Neovim Config
~/.config/nvim/
├── init.lua ← entry point
├── lua/
│ ├── config/
│ │ ├── options.lua ← vim.opt settings
│ │ ├── keymaps.lua ← vim.keymap.set
│ │ └── autocmds.lua ← vim.api.nvim_create_autocmd
│ └── plugins/
│ ├── telescope.lua ← Telescope config
│ ├── lsp.lua ← LSP config
│ └── ...