VS Code: The Features That Actually Save Time
Multi-cursor editing, the command palette, workspace settings and debugging — the VS Code features worth learning properly rather than the extension lists.
Table of contents
- Navigation you should not use the mouse for
- Multi-cursor: the highest-leverage editing feature
- Line operations
- Settings worth changing
- Commit these to the repo
- Debugging beats console.log
- Frequently asked questions
- How do I learn shortcuts I keep forgetting?
- Should I use the built-in terminal?
- Which extensions are actually worth it?
- How do I get the same setup on another machine?
- Related reading
- References
Most VS Code advice is a list of extensions. The built-in features below save more time than any extension, and they are the same across every project.
Navigation you should not use the mouse for#
| Shortcut | What it does |
|---|---|
Cmd/Ctrl+P | Open any file by fuzzy name |
Cmd/Ctrl+Shift+P | Command palette — every command |
Cmd/Ctrl+Shift+O | Jump to a symbol in this file |
Cmd/Ctrl+T | Jump to a symbol in the whole project |
F12 | Go to definition |
Shift+F12 | Find all references |
Cmd/Ctrl+Shift+\ | Jump to matching bracket |
Ctrl+- / Ctrl+Shift+- | Navigate back / forward through jump history |
The back/forward pair is the underused one: it makes "follow this definition three levels deep, then return" free.
In the quick-open prompt, Cmd+P then :42 jumps to a line, and @symbol filters symbols. Typing > turns it into the command palette.
Multi-cursor: the highest-leverage editing feature#
Cmd/Ctrl+D select next occurrence of the current selection
Cmd/Ctrl+Shift+L select ALL occurrences at once
Alt+Click add a cursor anywhere
Alt+Cmd/Ctrl+↑/↓ add a cursor above/below
Shift+Alt+drag column (box) selectionCmd+D repeatedly is better than find-and-replace for most renames, because you see each occurrence before committing and can skip one with Cmd+K Cmd+D.
For a rename that must be semantically correct, use F2 (Rename Symbol) instead — it understands scope and updates imports across files, which text replacement does not.
Line operations#
Alt+↑/↓ move the current line up/down
Shift+Alt+↑/↓ duplicate the line
Cmd/Ctrl+Shift+K delete the line
Cmd/Ctrl+Enter insert a line below without splitting the current one
Cmd/Ctrl+/ toggle comment
Shift+Alt+F format the documentAlt+↑/↓ on a selection moves the whole block, which is the fastest way to reorder object properties or JSX attributes.
Settings worth changing#
{
// Format and fix on save — removes an entire category of review comment
"editor.formatOnSave": true,
"editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" },
// Organise imports automatically
"editor.codeActionsOnSave": { "source.organizeImports": "explicit" },
// Use the workspace TypeScript version, not the bundled one.
// Prevents "works in CI, errors in my editor" mismatches.
"typescript.tsdk": "node_modules/typescript/lib",
// Prefer relative imports being rewritten to your path aliases
"typescript.preferences.importModuleSpecifier": "non-relative",
// Show whitespace only where it matters
"editor.renderWhitespace": "boundary",
// Bracket pair colouring, built in — no extension needed
"editor.guides.bracketPairs": "active",
// Do not index build output
"search.exclude": { "**/node_modules": true, "**/.next": true, "**/dist": true },
"files.watcherExclude": { "**/node_modules/**": true, "**/.next/**": true },
}The typescript.tsdk line is the one that prevents a real class of confusion: without it, your editor may use a different TypeScript version from your build, and they will disagree about errors.
The watcherExclude entries measurably reduce CPU usage on a large project.
Commit these to the repo#
.vscode/settings.json workspace settings everyone shares
.vscode/extensions.json recommended extensions, prompted on open// .vscode/extensions.json
{ "recommendations": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode"] }This is how you stop "it formats differently on my machine" without a conversation.
Debugging beats console.log#
A launch.json for a Next.js app:
{
"version": "0.2.0",
"configurations": [
{
"name": "Next.js: debug full stack",
"type": "node-terminal",
"request": "launch",
"command": "npm run dev"
}
]
}Then set breakpoints in the editor. Two features that make this decisively better than logging:
- Conditional breakpoints. Right-click a breakpoint and add
id === 'abc'— it only pauses on the case you care about, instead of 500 times. - Logpoints. A breakpoint that logs an expression without pausing and without editing the file. No stray
console.logto forget about.
Frequently asked questions#
How do I learn shortcuts I keep forgetting?#
Cmd+Shift+P and search for the command by name. The palette shows its shortcut next to it, so you learn them by using them.
Should I use the built-in terminal?#
Yes — Ctrl+\`` toggles it, and it opens in the workspace root. Splitting it (Cmd+`) for dev server and git in one view saves window switching.
Which extensions are actually worth it?#
ESLint and Prettier for consistency, plus a language server for anything not built in. Beyond that, most extensions cost startup time for marginal benefit — check the "Extension Bisect" and startup-performance views if the editor feels slow.
How do I get the same setup on another machine?#
Settings Sync, built in, via a GitHub account. Workspace settings should be in the repo regardless.