Becoming Productive in Neovim in 20 Minutes
How to set up Neovim for optimal productivity with a minimal, efficient configuration that works for web developers, note-takers, and anyone who wants to code faster.
Becoming productive in almost anything requires first identifying your bottlenecks and optimizing for them. With Neovim, your goal should be figuring out what you want to do and the least number of keystrokes to achieve it.
Many developers hesitate to try Neovim, thinking it will slow them down. But I’m here to tell you that learning Neovim (or even just Vim motions) can make you a better developer. Vim motions are a lifelong skill that can be applied to anything text-related and continuously improved over time.
Getting Started: My Configuration Approach
Instead of using a pre-configured Neovim setup (which is fine for beginners), I recommend building your own configuration. You learn much more by installing plugins yourself, and it takes only about 10 minutes to get a solid setup that suits your needs.
Let’s look at my configuration structure, which you can adapt to your preferences:
├── init.lua # Entry point
├── lua/
│ ├── globals.lua # Global functions and variables
│ ├── ui.lua # UI-related settings
│ ├── options.lua # Vim options
│ ├── config/ # Plugin configurations
│ ├── plugins/ # Plugin specifications
│ └── utils/ # Utility functions
Essential Components
1. Plugin Management with lazy.nvim
Start with a plugin manager like lazy.nvim to easily install and manage plugins. Here’s how I set it up in my 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({
{ import = 'plugins' },
}, {
change_detection = { notify = false },
performance = {
cache = { enabled = true },
defaults = { lazy = true },
checker = { enabled = false },
rtp = {
-- stylua: ignore
disabled_plugins = {
"gzip", 'netrw',"netrwPlugin",
"rplugin", "tarPlugin",
"tutor", "zipPlugin",
},
},
},
})
2. LSP Setup for Language Support
The LSP (Language Server Protocol) is what powers code intelligence - completion, diagnostics, and more. Set up Mason.nvim for easy language server management:
return {
{
'williamboman/mason.nvim',
cmd = 'Mason',
build = ':MasonUpdate',
dependencies = 'nvim-lua/plenary.nvim',
opts = { ui = { height = 0.8 } },
},
{
'williamboman/mason-lspconfig.nvim',
event = { 'BufReadPre', 'BufNewFile' },
dependencies = {
'mason.nvim',
{
'neovim/nvim-lspconfig',
dependencies = {
{
'folke/neodev.nvim',
ft = 'lua',
opts = {},
},
{
'folke/neoconf.nvim',
cmd = { 'Neoconf' },
opts = { local_settings = '.nvim.json', global_settings = 'nvim.json' },
},
},
},
},
opts = {
automatic_installation = true,
handlers = {
function(name)
local config = require('servers')(name)
if config then require('lspconfig')[name].setup(config) end
end,
},
},
},
}
3. Enhancing Editing with Custom Keymaps
Efficient editing is the heart of Vim productivity. Here’s how I set up some enhanced editing capabilities:
-- Define keys for all plugins
local keys = {
dial = {
{ '<C-x>', '<Plug>(dial-decrement)', mode = 'n' },
},
multiple_cursors = {
{ '<S-n>', '<Cmd>MultipleCursorsAddJumpNextMatch<CR>', mode = { 'x' }, desc = 'Add cursor and move down' },
},
align = {
{
'aa',
function() require('align').align_to_char({ length = 1 }) end,
desc = 'Align to character',
mode = 'x',
},
},
treesj = {
{
'<S-k>',
function() require('treesj').toggle({ split = { recursive = true } }) end,
desc = 'Join/split lines (Treesj)',
mode = 'n',
},
},
}
return {
{ 'vidocqh/auto-indent.nvim', event = 'BufReadPre' },
{
'monaqa/dial.nvim',
event = 'BufReadPre',
keys = keys.dial,
config = function()
local augend = require('dial.augend')
local config = require('dial.config')
config.augends:register_group({
default = {
augend.integer.alias.decimal,
augend.constant.alias.bool,
augend.constant.new({ elements = { 'let', 'const' } }),
augend.constant.new({ elements = { 'yes', 'no' } }),
},
})
end,
},
{
'kylechui/nvim-surround',
version = '*',
opts = { move_cursor = true, keymaps = { visual = 's' } },
event = 'BufReadPre',
},
}
4. Completion and AI Assistance
Modern development often leverages AI tools. Here’s how I integrate AI assistance with my nvim setup:
return {
{
'saghen/blink.cmp',
version = '*',
dependencies = {
'rafamadriz/friendly-snippets',
'MahanRahmati/blink-nerdfont.nvim',
{
'fang2hou/blink-copilot',
opts = {
kind_icon = completion_icons.Copilot,
},
},
},
opts = {
sources = {
default = {
'lsp',
'copilot',
'path',
'snippets',
'buffer',
'nerdfont',
},
},
},
},
{
'zbirenbaum/copilot.lua',
config = function()
require('copilot').setup({
suggestion = { enabled = false },
panel = { enabled = true },
filetypes = {
markdown = true,
help = true,
},
})
end,
},
{
'yetone/avante.nvim',
opts = {
provider = 'claude',
windows = {
position = 'right',
input = {
prefix = '> ',
height = 8,
},
},
hints = {
enabled = false,
},
},
},
}
5. Navigation Made Easy
Efficient file and code navigation is crucial for productivity:
return {
{
'ibhagwan/fzf-lua',
config = function()
local fzf = require('fzf-lua')
local prompt = icons.misc.fzf
fzf.setup({
prompt = prompt,
winopts = {
border = vim.g.neovide and 'empty' or 'single',
},
actions = {
files = {
['enter'] = actions.file_edit_or_qf,
['ctrl-s'] = actions.file_vsplit,
},
},
})
end,
keys = {
{ '<c-f>', function() reqcall('fzf-lua').files() end },
{ '<c-g>', function() reqcall('fzf-lua').live_grep({ resume = true }) end },
{ '<c-.>', function() reqcall('fzf-lua').lsp_code_actions() end },
{ '<leader>ff', function() reqcall('fzf-lua').git_files() end },
},
},
{
'nvim-neo-tree/neo-tree.nvim',
config = function() require('config.neo-tree') end,
dependencies = {
'mrbjarksen/neo-tree-diagnostics.nvim',
'MunifTanjim/nui.nvim',
},
keys = {
{
'<M-e>',
'<cmd>Neotree toggle<cr>',
mode = 'n',
},
},
},
}
My Custom Keymaps for Faster Workflow
Your productivity will skyrocket with some custom keymaps. Here are some I’ve found invaluable:
-- Search for visually selected text
keymap('v', '//', [[y/\V<C-R>=escape(@",'/\')<CR><CR>N]])
-- End/start of line shortcuts
keymap({ 'n', 'v' }, '<M-l>', '$') -- End of line
keymap({ 'n', 'v' }, '<M-h>', '^') -- Start of line
-- Clipboard operations
keymap({ 'n', 'v' }, '<C-v>', '"+p') -- Paste
keymap('i', '<C-v>', '<C-r>+') -- Paste (insert mode)
keymap('v', '<C-c>', '"+y') -- Yank
-- Buffer operations
keymap('n', '<S-s>', '<cmd>silent w<cr>') -- Save
keymap('n', '<S-q>', '<cmd>bd<cr>') -- Quit
keymap('n', '<C-CR>', '<C-^>') -- Swap
-- Folding shortcuts
keymap('n', 'zh', 'za') -- Collapse at cursor
keymap('n', 'zl', 'zA') -- Expand at cursor
keymap('n', 'zH', 'zM') -- Collapse all
keymap('n', 'zL', 'zR') -- Expand all
Adding UI Enhancements with Neovide
If you want a sleek, modern UI experience, consider using Neovide with Neovim. It provides cursor effects, smooth scrolling, and other visual enhancements:
-- Neovide specific configurations
vim.g.neovide_refresh_rate = 144
vim.g.neovide_cursor_antialiasing = true
vim.g.neovide_cursor_animate_command_line = false
vim.g.neovide_cursor_trail_size = 0.6
vim.g.neovide_cursor_animation_length = 0.05
vim.g.neovide_cursor_vfx_mode = 'pixiedust'
vim.g.neovide_cursor_vfx_particle_density = 40.4
vim.g.neovide_cursor_vfx_particle_speed = 10.0
vim.opt.guifont = 'Cartograph CF Light,Symbols Nerd Font:h13.4:w1.4'
opt.linespace = 11
The Power of Statusline
A custom statusline can provide valuable context while you work. Here’s a snippet of my setup:
function M.render()
local curwin = api.nvim_get_current_win()
local curbuf = api.nvim_win_get_buf(curwin)
local is_active = curwin == vim.g.actual_curwin
local ctx = {
bufnum = curbuf,
win = curwin,
bufname = api.nvim_buf_get_name(curbuf),
preview = vim.wo[curwin].previewwindow,
readonly = vim.bo[curbuf].readonly,
filetype = vim.bo[curbuf].ft,
buftype = vim.bo[curbuf].bt,
modified = vim.bo[curbuf].modified,
}
-- Custom elements and logic follow...
return display({ l1, r1 }, available_space - 5)
end
Conclusion
Becoming productive in Neovim doesn’t have to take months or years. With a focused approach on the core functionality you need, you can be up and running in 20 minutes. The key is to start small, build incrementally, and be consistent in practicing your Vim motions.
Remember that Vim motions are a lifelong skill that will continue to improve over time. The investment you make now will pay dividends throughout your career, making you more efficient regardless of what you’re coding or writing.
What are you waiting for? Give Neovim a try today and start your journey to text-editing mastery!
For more tips and tricks on improving your Neovim workflow, check out my other blog posts or watch the full video tutorial on my channel.