Skip to content
Happy Programming Guide
Start learning
Coding Tools

Multiline Replace Techniques in VS Code

How to find and replace across several lines in VS Code: regex mode, newline escapes, capture groups, multi-cursor editing and replacing across a whole folder.

Colour-highlighted code on a monitor

VS Code can replace text that spans several lines, but only in regex mode — you turn it on with the .* button in the search box, then use \n for a line break. That one detail is what most people are missing. This guide covers that, capture groups, multi-cursor editing as an alternative, and replacing across an entire project safely.

The setting that makes it work#

Open find and replace with Ctrl+H (Cmd+Option+F on a Mac), then click the .* icon at the right of the search field, or press Alt+R. Without regex mode on, the search box treats everything literally and a newline cannot be typed at all.

Once it is on, these escapes work:

Escape Matches
\n a line break
\r\n a Windows line break
\s any whitespace, newlines included
\t a tab
. any character except a newline
[\s\S] genuinely any character

That last row is the important one. A dot does not match line breaks, so a pattern like start.*end will never span lines. Use [\s\S]*? instead.

A first example#

Turning two lines into one:

Output
Find:     console.log\(\n\s*(.+)\n\s*\);
Replace:  console.log($1);

And collapsing blank lines — three or more become one:

Output
Find:     \n{3,}
Replace:  \n\n

You can also press Shift+Enter inside the find box to insert a literal newline, which is sometimes easier to read than typing the escape.

Lazy versus greedy#

This is the mistake that produces alarming results:

Output
Find:  <div>[\s\S]*</div>      Greedy - matches to the LAST closing tag
Find:  <div>[\s\S]*?</div>     Lazy - stops at the FIRST one

The question mark after the star makes the match as short as possible. On a file with twenty divs, the greedy version selects everything from the first opening tag to the last closing one — and replacing that deletes most of the file.

Capture groups#

Anything in brackets is captured and available as $1, $2 and so on in the replacement.

Swapping two lines:

Output
Find:     (import .+)\n(import .+)
Replace:  $2\n$1

Converting a function declaration to an arrow function:

Output
Find:     function (\w+)\((.*?)\) \{
Replace:  const $1 = ($2) => {

Turning a two-line assignment into a single line:

Output
Find:     const (\w+) =\n\s+(.+);
Replace:  const $1 = $2;

Named groups read better in long patterns:

Output
Find:     (?<key>\w+):\s*(?<value>.+)
Replace:  $<value> belongs to $<key>

Case conversion in replacements#

VS Code supports four modifiers that other editors do not:

Output
$1   the captured text
\u$1  uppercase the first character
\l$1  lowercase the first character
\U$1  uppercase all of it
\L$1  lowercase all of it
Output
Find:     const (\w+) =
Replace:  const \U$1 =         turns names into CONSTANT_CASE

Multi-cursor editing, often the better tool#

A regex is not always the answer. For repetitive edits on similar lines, multiple cursors are faster and much easier to verify:

Shortcut Does
Ctrl+D select the next occurrence of the current selection
Ctrl+Shift+L select every occurrence at once
Alt+Click add a cursor wherever you click
Ctrl+Alt+Down add a cursor on the line below
Shift+Alt+I a cursor at the end of every selected line
Alt+Shift+drag column selection

Select several lines and press Shift+Alt+I, and you get one cursor at the end of each — ideal for adding a comma or a semicolon to every line at once. On a Mac these use Cmd and Option.

Sorting and joining lines#

Two commands worth knowing, from the palette (Ctrl+Shift+P):

  • Join Lines (Ctrl+J) — merges the selected lines into one
  • Sort Lines Ascending — useful for import blocks and lists

Both are often quicker than the regex you were about to write.

Across a whole folder#

Ctrl+Shift+H opens search across files. The same regex rules apply, plus two fields that matter a great deal:

Output
files to include:  src/**/*.ts
files to exclude:  **/node_modules/**, **/dist/**, **/*.min.js

VS Code respects .gitignore by default, but being explicit costs nothing and prevents replacing text inside built output or dependencies.

The results panel shows every match grouped by file before you replace. Expand it and read a few. You can also dismiss individual files or matches from the results with the cross icon, which excludes them from the replace.

Patterns worth keeping#

Output
Trailing whitespace
Find:     [ \t]+$
Replace:  (empty)

Empty lines at the end of a block
Find:     \n\s*\n(\s*\})
Replace:  \n$1

Remove a whole multi-line comment
Find:     /\*[\s\S]*?\*/
Replace:  (empty)

Wrap every line in quotes and a comma
Find:     ^(.+)$
Replace:  "$1",

Change double-quoted imports to single quotes
Find:     from "(.+?)"
Replace:  from '$1'

When not to use find and replace#

For renaming a symbol, use F2 instead. It understands the language, so it renames the variable and every reference to it without touching a string that happens to contain the same word. A regex cannot tell those apart.

Similarly, for reformatting, run the formatter (Shift+Alt+F) rather than writing patterns to fix indentation.

Questions people ask#

Why does my newline escape not work?

Regex mode is off. The .* button must be highlighted, or the escapes are treated as literal characters.

Can I search across lines without regex?

You can paste multi-line text directly into the find box and VS Code will match it literally. That works for exact blocks but gives you no flexibility.

How do I match a literal dollar sign in the replacement?

Write $$. A single dollar introduces a capture group reference.

Does this work in the terminal too?

No — the terminal has its own search with no replace. For file-wide edits from the command line, sed or ripgrep with a replacement flag are the equivalents.

Where to go next#

Git and GitHub, so a bad replace is never permanentRead next

Keep reading

Keep going — pick your next guide

The fastest way to improve is to read one guide, then build the thing it describes. Start with the basics, or jump straight to a project.

Ask a question or share what worked

Your email address will not be published. Required fields are marked *