
Commenting out a block of lines is one of those edits you make constantly: disabling a section of a config file, switching off a function while debugging, or keeping an old approach around while testing a new one. In most editors that is select plus a single shortcut. Classic Vim has no comment shortcut out of the box, but it has three techniques that are just as fast once you know them, and recent versions ship a toggle plugin as well.
This guide shows how to comment out multiple lines in Vim with visual block mode, the substitute command, and the built-in comment plugin, and how to remove the comments again with each method.
Commenting with Visual Block Mode
The classic answer uses visual block mode to insert a comment character at the start of every selected line:
- Move the cursor to the first column of the first line you want to comment.
- Press
Ctrl+Vto enter visual block mode. - Press
j(or the down arrow) until every target line is selected. - Press
Shift+Ito insert at the left edge of the block. - Type the comment character,
#for shell scripts and config files. - Press
Esc.
On Windows, Ctrl+V may be mapped to paste. If it does not start visual block mode, use Ctrl+Q instead.
The # appears on the first line while you type, which looks like the operation failed. It did not: the moment you press Esc, Vim repeats the insert on every line of the block.
For languages with two-character comments, type the whole prefix in step 5, for example // for C, JavaScript, and Go, or -- for SQL and Lua. The entire string is inserted on each line.
Because this selection starts in the first column, I prefixes every selected line, including empty lines. If you start the block farther to the right, Vim skips lines that end before the block’s left edge. Use the substitute method below when you want to prefix a line range regardless of line length.
Uncommenting with Visual Block Mode
The reverse uses the same block selection to delete a column of characters:
- Place the cursor on the first comment character of the first line.
- Press
Ctrl+V, thenjdown to the last commented line. - If the comment prefix is longer than one character, press
lonce for each additional character to widen the block over the whole prefix. - Press
xordto delete the selected block on every line.
The highlighted column disappears from all lines at once. If you also inserted a space after the comment character, include it in the block before deleting.
Commenting with the Substitute Command
The substitute command edits a range of lines in one pass, which makes it the better tool when you already know the line numbers or when the block is large. To comment lines 10 through 20:
vi
:10,20s/^/#/
The range 10,20 limits the substitution to those lines, ^ matches the empty start of each line, and the replacement inserts # there. Unlike block insert, this prefixes every line in the range, including blank ones.
You can also select the lines first with V and a movement, then press :. Vim inserts the '<,'> range for you, and you complete the command:
vi
:'<,'>s/^/#/
Other ranges work the same way: % for the whole file and .,+4 for the current line and the four below it. If you use these often, turning on line numbers makes ranges much quicker to read off the screen; see How to Show Line Numbers in Vim
.
To uncomment, delete the leading character instead of inserting it:
vi
:10,20s/^#//
This removes the first # of each line in the range and leaves lines without one untouched. For a two-character prefix such as //, escaping the slashes gets noisy, so switch the delimiter to another punctuation character:
vi
:'<,'>s#^//##
The substitute command accepts almost any punctuation as its separator, and using # here keeps the // readable. The full syntax, including flags and confirmation mode, is covered in Vim Find and Replace
.
Keeping Indentation with :normal
Both methods above put the comment character in column one. To insert it in front of the first non-blank character instead, preserving the code’s indentation profile, run a normal mode command over the range:
vi
:'<,'>norm I#
The :norm command replays its argument as keystrokes on every line in the range, and I in normal mode enters insert mode at the first non-blank character, so each line gets # right before its content rather than at the margin. Undo treats the whole operation as a single change, so u reverts every line at once.
Toggling Comments with gc
Modern Vim and Neovim can toggle comments with a mapping that knows each filetype’s comment syntax. Neovim 0.10 and later has it built in with no setup: select lines with V and press gc, or use gcc for the current line and gc followed by a motion, such as gcip for a paragraph. Pressing gc on commented lines uncomments them.
Vim 9.1.0375 and later bundles a similar optional comment package. Enable it for the current session with :packadd comment, or add this line to your vimrc to load it on startup:
~/.vimrcvim
packadd comment
After loading the package, the gc mappings work as described above and pick the right comment string automatically: # in a shell script, // in C, and " in a vimrc. On older Vim versions, the widely used commentary.vim
plugin provides the same familiar mappings.
If you comment code many times a day, this is the method worth adopting, since toggling with gc replaces both the comment and uncomment workflows in one mapping.
Quick Reference
For a printable quick reference, see the Vim cheatsheet
.
| Action | Keys or command |
|---|---|
| Comment a block | Ctrl+V, select lines, Shift+I, type #, Esc |
| Uncomment a block | Ctrl+V, select the comment column, x |
| Comment lines 10-20 | :10,20s/^/#/ |
| Comment a visual selection | :'<,'>s/^/#/ |
| Uncomment lines 10-20 | :10,20s/^#// |
| Comment after indentation | :'<,'>norm I# |
| Toggle comments (Neovim 0.10+, Vim 9.1.0375+) | select with V, press gc |
| Toggle the current line | gcc |
| Undo the whole operation | u |
Conclusion
For an occasional block, Ctrl+V, Shift+I, Esc is the fastest habit to build, and :s/^/#/ handles precise ranges and blank lines. If commenting is part of your daily editing, enable the gc mappings and get toggling in both directions. For selecting larger regions to operate on, see How to Select All in Vim
.