Tools Viewer

How to Match Newlines and Line Breaks in Regex (\n, \r, \r\n Explained)

Published on September 13, 2026 by Hasnain

How to Match Newlines and Line Breaks in Regex (\n, \r, \r\n Explained)
How to Match Newlines and Line Breaks in Regex (\n, \r, \r\n Explained)

A regex newline pattern matches line breaks inside a string or file. The tricky part is that a line break isn't always one character: Unix and modern macOS text usually uses \n, old Mac text used \r, and Windows text commonly uses \r\n. A pattern that works on one pasted sample can fail on logs, CSV files, email text, or copied terminal output if it assumes the wrong newline style.

This guide covers \n, \r, \r\n, the dotAll (s) and multiline (m) flags, the portable [\s\S] trick for matching across lines, and a JavaScript-specific quirk that trips up a lot of developers: how the $ anchor behaves with a trailing newline even when the m flag is off. You can test every example in RegEx Visualizer, a browser regex tester from Tools Viewer that matches with JavaScript RegExp, plus Replace mode and presets. Your regex pattern and sample text stay on-device.

Quick Answer: How to Match a Line Break

If you need to match a line break in JavaScript and most common regex engines, start with \r?\n. It matches a Unix line feed and a Windows carriage-return-plus-line-feed pair. If you also need to accept isolated carriage returns, use \r\n?|\n. If your engine supports \R, as PCRE2 does, that represents a broader newline sequence — but it is not a JavaScript feature.

  • \n means line feed, often called LF.
  • \r means carriage return, often called CR.
  • \r\n means CRLF, common in Windows files and many network formats.
  • \r?\n means optional CR followed by LF, so it matches both LF and CRLF.
  • \r\n?|\n matches CRLF, a lone CR, or LF.

The right choice depends on where the text comes from. Browser textareas often normalize line endings before your JavaScript sees them. Files, copied log excerpts, and email sources may preserve mixed endings. When you're unsure, paste real examples into the tester and check visible matches instead of guessing from the operating system.

JavaScript Regex $ and the Final Newline Without the m Flag

This is one of the most misunderstood parts of JavaScript regex, and it catches even experienced developers off guard: in JavaScript, the $ anchor without the m flag does not strictly mean "end of the entire string." It means end of the string, or immediately before a trailing line terminator at the very end of the string.

In other words, a pattern like /foo$/ will match "foo" in the string "foo" and will also match "foo" in the string "foo\n" — even though m is not set. This differs from what many developers expect and from how some other regex engines behave.

Why this happens:

  • Without m, $ normally asserts "end of input."
  • JavaScript's ECMAScript spec carves out a special case: if the very last character of the string is a line terminator (\n, \r, \u2028, or \u2029), $ can also match just before that trailing character.
  • This only applies to a newline at the absolute end of the string. It does not turn $ into a per-line anchor. A newline in the middle of the string is ignored by $ unless m is set.

How to test it yourself:

  1. Open RegEx Visualizer.
  2. Enter the pattern foo$ with no flags set.
  3. Test it against the string "foo" — it matches.
  4. Test it against the string "foo\n" — it also matches, because \n is a trailing line terminator.
  5. Test it against the string "foo\nbar" — it does not match "foo", because the newline is no longer at the very end of the string.
  6. Now add the m flag and test "foo\nbar" again — with m, $ matches at the end of every line, so "foo" now matches even though it isn't at the end of the string.

This distinction matters most when you're validating line endings, checking whether a string "ends with" something, or writing a pattern meant to reject trailing whitespace or blank lines.

Why Dot Does Not Match Newline

In most regex engines, the dot (.) means any character except a line terminator. That's why a pattern like <.*> may work for one-line HTML but fail as soon as the opening and closing tags are separated by line breaks. The dot isn't broken — it's following the default rule.

To match any character including newline, choose one of these approaches:

  • Enable the s flag, also called dotAll, so . can match line breaks.
  • Use [\s\S], a character class meaning whitespace or non-whitespace, which covers every character.
  • In some flavors, use an engine-specific mode or token — but confirm your production engine supports it before relying on it.

Example:

/<!--[\s\S]*?-->/ finds an HTML comment that spans multiple lines. The *? quantifier is non-greedy, so it stops at the next closing marker instead of racing to the last one in the string. In RegEx Visualizer, add two comments to your sample text and confirm the pattern returns two matches, not one giant match.

DotAll (s) Flag vs Multiline (m) Flag

The s and m flags solve two different newline problems, and confusing them is one of the most common regex mistakes.

  • The s flag changes dot behavior:.can now match line breaks.
  • The m flag changes anchor behavior: ^ and $ can match the start and end of each line, not only the start and end of the whole string.

For example:

^ERROR with the m and g flags finds every line that begins with "ERROR." Without m, it only matches if the entire string begins with that word. By contrast, ERROR.*END needs the s flag if "ERROR" and "END" may appear on different lines. Many real-world patterns use both flags together — m to locate line boundaries, and s to let a section's body span multiple lines.

Using Anchors for Line-by-Line Matching

Newline questions often come down to anchors. Use ^.+$ with the m flag to match non-empty lines, or ^\s*$ with m to find blank or whitespace-only lines. Remember that \s includes newlines, so be careful when a line pattern contains \s* — if that token can consume the line break itself, the match may stretch farther than expected.

When the goal is to capture whole lines, prefer patterns that explicitly exclude line breaks, such as [^\r\n]+. That character class means "one or more characters that are not a carriage return and not a line feed." It's often clearer than relying on dot behavior, especially when a teammate might later toggle the s flag and unintentionally change matching elsewhere in the pattern.

Splitting, Replacing, and Normalizing Newlines

Replace mode is the easiest way to see exactly what a newline regex will do before you run it on real data.

  • To normalize mixed line endings to LF: match \r\n?|\n and replace with the newline character your target language expects.
  • To remove blank lines: start with ^\s*\r?\n using the g and m flags, then test against lines that contain only spaces or tabs to confirm they're caught too.
  • To join wrapped lines without destroying paragraph breaks: don't blindly replace every \r?\n with a space. First decide what counts as a paragraph boundary. A common approach replaces single line breaks with a space but leaves two or more line breaks intact. A starting pattern for double breaks is (?:\r?\n){2,}, but real text often includes blank lines that contain trailing spaces, so you may need (?:\r?\n[ \t]*){2,} instead.

JavaScript Flags and Export Quoting

RegEx Visualizer matches using JavaScript's RegExp engine. Modern browsers support the s and m flags for every example on this page. Tokens such as \R are a PCRE concept and are not available in JavaScript — stick to \r?\n or \r\n?|\n when you need portable newline matching in the tool.

The tester shows match behavior, but host language syntax still matters. In JavaScript source code, a regex literal like /\r?\n/g isn't written the same way as a string passed to new RegExp(). Export snippets for other languages are copy-paste helpers for quoting and API shape, not alternate match engines. Confirm the pattern's behavior in the visualizer first, then adapt the string quoting for your target codebase.

Whitespace Overlap

\s already includes newline characters. That's useful for matching any kind of spacing but risky when you only meant a literal space. If you're cleaning text and want spaces or tabs without crossing lines, use [ \t] instead of \s.

The opposite also matters: if a pattern uses \S+, it stops at newlines, because newlines count as whitespace. That makes \S+ useful for matching tokens or words, but not for matching whole paragraphs. Use [\s\S]+? or the dotAll flag when a match needs to cross line boundaries. For a deeper breakdown of whitespace matching in general, see our guide on regex space and whitespace.

Try It Step by Step

  1. Open RegEx Visualizer.
  2. Paste a multi-line sample with a blank line, an indented line, and text that should not be matched.
  3. Try \n, then \r?\n, then \r\n?|\n.
  4. Toggle the m flag and test ^ and $ line anchors across multiple lines.
  5. Toggle the s flag and compare . against [\s\S].
  6. Test foo$ with no flags against "foo", "foo\n", and "foo\nbar" to see the trailing-newline exception in action.
  7. Open Replace mode to preview newline normalization before changing real text.

FAQs

Does $ match the end of a line or the end of a string in JavaScript regex?
Without the m flag, $ matches the end of the entire string. The one exception is a line terminator sitting at the very end of the string — JavaScript lets $ match immediately before that trailing newline even without m. With the m flag set, $ matches the end of every line in the string.
Why does my pattern match a string ending in \n even though I didn't use the m flag?
This is expected JavaScript behavior. $ has a built-in exception for a single trailing line terminator at the end of the string, even when m is off. It does not mean m is being applied elsewhere — a newline in the middle of the string is still ignored by $ without m.
How do I match a strict end of string that ignores the trailing newline exception?
Strip the trailing line terminator first with something like .replace(/\r?\n$/, ""), then run your $-anchored pattern, or compare directly against the string's expected length instead of relying on $.
What is the difference between \n, \r, and \r\n?
\n is a line feed, used by Unix and modern macOS. \r is a carriage return, used by old classic Mac systems. \r\n is a carriage-return-plus-line-feed pair, standard on Windows and common in network protocols like email and HTTP headers.
Why doesn't the dot (.) match a newline by default?
Regex engines treat . as "any character except a line terminator" by default, specifically so that patterns don't accidentally span multiple lines. Enable the s (dotAll) flag, or use the [\s\S] character class, when you need a match to cross line breaks.
Is \R the same as \r\n in JavaScript?
No. \R is a PCRE-only token that represents a broader set of newline sequences. JavaScript's RegExp engine does not support \R — use \r?\n or \r\n?|\n instead for portable newline matching.