Vim has a reputation for being hard to learn. That reputation is earned — the learning curve is real. But the payoff is also real: once the motions click, editing text in Vim is faster and more precise than anything else.
This post focuses on the five techniques that deliver the most value for developers — not survival tips, but the things that make Vim genuinely superior.
Why Vim Works the Way It Does
Vim’s power comes from composability. Commands follow a grammar:
[count] [operator] [motion/text-object]
d3w— delete 3 wordsci"— change inside “quotes”y$— yank from cursor to end of line
Once you internalize this grammar, you’re not memorizing individual commands — you’re constructing sentences. That’s why experienced Vim users seem to edit code at thought speed.
Tip 1: Master Motions — Stop Using Arrow Keys
Arrow keys and hjkl for character-by-character movement is a trap. The real power is in semantic motions:
| Motion | Description |
|---|---|
w / b | Next / previous word start |
e / ge | Next / previous word end |
f{char} / F{char} | Jump forward/back to character on line |
t{char} / T{char} | Jump to just before/after character |
; / , | Repeat last f/t forward/backward |
% | Jump to matching bracket/paren/brace |
{ / } | Jump to previous/next empty line (paragraph) |
gg / G | Jump to start/end of file |
5G or :5 | Jump to line 5 |
H / M / L | Jump to top / middle / bottom of viewport |
Ctrl-d / Ctrl-u | Scroll down / up half page |
The f Motion is Underrated
The quick brown fox jumps over the lazy dog
^ cursor here
ft → jumps to 't' in "the" (next occurrence)
; → jumps to 't' in "the lazy"
Combine with operators: dft deletes from cursor to the next t. ct; changes from cursor to the next semicolon.
Search as a Motion
/ and ? are also motions:
" Delete from cursor to the next occurrence of "function"
d/function<Enter>
" Change from cursor to previous opening brace
c?{<Enter>
Tip 2: Text Objects — Edit Semantic Units
Text objects are what separate Vim from every other editor. They let you operate on semantic units of code, not just character ranges.
The pattern is [operator]i[object] (inside) or [operator]a[object] (around/including).
| Text Object | What It Selects |
|---|---|
iw / aw | inner word / word + surrounding space |
is / as | inner sentence / sentence + space |
ip / ap | inner paragraph / paragraph + blank line |
i" / a" | inside quotes / quotes + content |
i' / a' | inside single quotes |
i( / a( | inside parens () / including parens |
i[ / a[ | inside brackets [] |
i{ / a{ | inside braces {} |
it / at | inside HTML/XML tag / including tags |
Real-World Examples
// Cursor anywhere inside the string:
const greeting = "Hello, World!";
// ^ cursor
ci" // Change Inside quotes → replaces "Hello, World!" and enters insert mode
da" // Delete Around quotes → deletes the quotes AND "Hello, World!"
yi" // Yank Inside quotes → copies "Hello, World!" to clipboard
// Cursor anywhere inside the function call:
console.log(someVariable, anotherOne);
// ^ cursor
di( // Delete Inside parens → console.log()
ca( // Change Around parens → console.log (deletes parens too, enters insert)
<!-- Cursor inside the tag: -->
<div class="container">Some text here</div>
<!-- ^ cursor -->
cit
<!-- Change Inside Tag → clears content, ready to type new content -->
dat
<!-- Delete Around Tag → removes the entire <div> including tags -->
This is the single feature that makes the biggest difference in day-to-day coding.
Tip 3: Macros — Automate Repetitive Edits
Macros record a sequence of keystrokes and replay them. They’re perfect for repetitive structural edits across multiple lines or files.
Recording a Macro
q{register} → start recording into register (a-z)
{your commands}
q → stop recording
@{register} → replay the macro
@@ → replay the last macro
100@a → replay macro 'a' 100 times
Practical Example: Transform an Array
You have this data:
const items = ["apple", "banana", "cherry"];
You want to convert each string to an object { name: 'apple' }:
qa → start recording into 'a'
0 → go to start of line
f' → jump to the single quote
i{ name: <Esc> → insert before the opening quote
A },<Esc> → append at end of line
j → move to next line
q → stop recording
@a → replay for 'banana'
@@ → replay for 'cherry'
Or just run 2@a to replay it on the next 2 lines at once.
Macro Tip: Run on Multiple Lines Efficiently
Select lines in visual mode, then :normal @a to run the macro on each selected line:
" Select lines 2-10 in visual mode, then:
:'<,'>normal @a
Tip 4: Marks — Instant Navigation Between Positions
Marks let you bookmark positions in files and jump back instantly.
m{letter} → set mark at current cursor position
'{letter} → jump to the line of mark
`{letter} → jump to the exact position of mark (line + column)
Local Marks (a-z) — Within a File
ma " mark current position as 'a'
" ... go somewhere else in the file ...
`a " jump back to exact position of mark 'a'
'a " jump to the line of mark 'a'
Global Marks (A-Z) — Across Files
" In config.ts:
mC " Set global mark 'C' (capital = global)
" In any other file later:
`C " Jump directly to config.ts, exact line and column
Global marks persist across sessions (if you have viminfo/shada enabled).
Special Marks You Should Know
| Mark | Description |
|---|---|
` | Position before last jump |
. | Position of last change |
[ | Start of last yanked/changed text |
] | End of last yanked/changed text |
< | Start of last visual selection |
> | End of last visual selection |
g; " jump to previous change location (cycle through change list)
g, " jump to next change location
Tip 5: The Dot Command — Repeat Without Thinking
The . (dot) command repeats the last change. It’s deceptively powerful when you understand what counts as a “change”:
- Any insert mode session (everything you typed before pressing
Esc) - Any operator + motion combination (
dw,ci", etc.) - Any
:ssubstitution
Simple Example
// You want to add a semicolon to the end of each of these lines:
const a = 1;
const b = 2;
const c = 3;
A;<Esc> → append semicolon to end of first line, exit insert
j. → move down, repeat (dot adds semicolon)
j. → move down, repeat again
The Real Power: Intentional Dot-Repeatable Commands
Design your edits so the dot command does the right thing. The key: enter insert mode once, do all your changes, then exit. That whole sequence becomes one dot-repeatable operation.
// Replace all console.log with logger.info — but only specific ones
// (not using global :s because you want control)
* " search for word under cursor (console)
cgnlogger.info<Esc> " cgn: change the next search match, type replacement
. " repeat: finds and changes the next match
. " repeat again
n. " skip one, then change the next
This cgn + . pattern is one of the most useful in Vim for selective search-and-replace.
Putting It All Together
Here’s a real refactoring scenario — converting a JS object to use shorthand property names:
// Before:
const obj = {
name: name,
age: age,
email: email,
};
// After:
const obj = {
name,
age,
email,
};
Using what we’ve learned:
/name: name<Enter> " search for the pattern
cgn<C-r>/<BS> … " or just use:
Actually, this is a great macro candidate:
qa " start macro
0 " go to line start
f: " jump to colon
dt, " delete from colon to comma (leaves " name,")
j " next line
q " end macro
2@a " repeat for age and email
Quick Reference
| Concept | Key Commands |
|---|---|
| Fast motions | f/t/F/T, w/b/e, {/} |
| Text objects | ci", da(, yit, vi{ |
| Macros | qa record, @a replay, @@ repeat |
| Marks | ma set, `a jump, mA global |
| Dot command | . repeat last change |
| Composition | d3w, c2i", 5>> |
Conclusion
Vim’s efficiency comes from composition: operators, motions, and text objects combine into a powerful editing language. These five tips — semantic motions, text objects, macros, marks, and the dot command — are where the real productivity gains live.
The learning investment is front-loaded, but once these become muscle memory, you’ll find yourself reaching for Vim (or Vim keybindings in VS Code/JetBrains) because editing feels faster and more precise than any other way.
💬 Want to learn, build, and grow with a community of developers? Join the King Technologies Discord — where code meets community!