skip to content

In JavaScript regular expressions, what do the m and s flags each change, and which one makes . match a newline?

level: middleimportance: should knowfreq 52%

answer

  1. one flag is about the dot
  2. the other is about anchors
  3. default dot skips four code points
  4. named for what it widens
  5. a class like [\s\S] needs no flag

basics

~20 s

The s flag, called dotAll, is the one that makes . match line terminators. The m flag leaves . completely alone; it changes ^ and $ so they match at every line boundary instead of only at the whole string's start and end.

solid answer

~40 s

They are unrelated despite being confused constantly. By default `.` matches any character **except** a line terminator — `\n`, `\r`, `
`, `
`. The `s` flag, added in ES2018 and exposed as `re.dotAll`, removes that exception so `.` matches absolutely any character. The `m` flag has nothing to do with `.`: it changes where `^` and `$` are allowed to match, from the string's ends only to the start and end of each line. So `/a.b/.test("a\nb")` is `false`, `/a.b/s.test("a\nb")` is `true`, and adding `m` instead does not help at all. The two are orthogonal and often used together when scanning multi-line text. Before `s` existed the standard workaround was a character class covering everything, such as `[\s\S]`, which still works and needs no flag.

go deeper

for a junior

Remember which letter does which: s widens the dot, m widens the anchors. Being able to say that . skips newlines by default already puts you ahead of most candidates.

for a middle

Explain the mechanics: name the four line terminators the dot excludes, show that dotAll removes that exclusion, and predict the output of a pattern with both flags on multi-line input.

for a senior

Demonstrate judgment about real text: normalise \r\n before matching, prefer [\s\S] when only one section should cross lines, and know that flags are read-only so a shared regex cannot be retuned in place.

for a principal

Own the wider call: multi-line scanning by regex is fragile against real-world input, and you should be able to say when a parser, a line splitter, or a purpose-built library beats a clever pattern that the next reader cannot verify.

## The default behaviour of the dot In a JavaScript regular expression, `.` is defined as "any character that is not a line terminator". The line terminators are exactly four code points: line feed `\n` (U+000A), carriage return `\r` (U+000D), line separator `
` (U+2028), and paragraph separator `
` (U+2029). Everything else — letters, digits, punctuation, tabs, ordinary spaces, emoji — is matched by `.`. That default exists because regular expressions historically operated line by line. In JavaScript, where you almost always hold the whole text in one string, the exclusion is more often a surprise than a help: ```js /a.b/.test("a\nb"); // false /a.b/.test("a b"); // true (space is not a line terminator) /a.b/.test("a\tb"); // true (tab is not either) ``` ## The s flag: dotAll Adding `s` removes the exception outright, so `.` matches every character including the four terminators. It was introduced in ES2018 and is reflected by the read-only boolean property `dotAll`: ```js const re = /a.b/s; re.dotAll; // true re.flags; // "s" re.test("a\nb"); // true ``` The name is the point: dotAll, dot matches all. It affects nothing else in the pattern — character classes, quantifiers, escapes and anchors behave exactly as before. Before ES2018, the idiomatic workaround was a character class whose two halves cover the whole character set, most commonly `[\s\S]` (whitespace or non-whitespace) or `[^]` (negated empty class, a JavaScript-specific spelling). Both still work today and both are flag-free, which makes them useful when you want *one part* of the pattern to cross lines while `.` elsewhere stays line-bounded: ```js /<pre>[\s\S]*?<\/pre>/.test("<pre>x\ny</pre>"); // true, no flag needed ``` ## The m flag: multiline The `m` flag is about a different mechanism entirely: where the `^` and `$` assertions are permitted to match. Without it, `^` matches only at position 0 of the input and `$` only at the very end. With `m`, they also match immediately after and immediately before any line terminator, so a pattern applies per line: ```js const text = "alpha\nbeta\ngamma"; text.match(/^\w+$/g); // null - no single line spans the whole string text.match(/^\w+$/gm); // ["alpha", "beta", "gamma"] ``` The corresponding property is `re.multiline`. Note what `m` does *not* do: it does not make `.` match newlines, and it does not split the input — the engine still sees one continuous string, it merely gains extra positions where the two anchors succeed. ## Why the confusion is so common The word "multiline" reads like "work across multiple lines", which is what people actually want when a pattern fails on multi-line input. The name describes the anchor behaviour, not the dot. Several other languages call the dot-matching flag DOTALL or `(?s)`, and JavaScript's single-letter `s` follows that tradition. The reliable mental model is: - `s` widens **`.`** - `m` widens **`^` and `$`** They compose freely. `/^.+$/gms` matches the entire string as one greedy run because `.` now crosses lines; `/^.+$/gm` matches each line separately. That pair is worth being able to predict out loud, because it demonstrates you understand both flags rather than having memorised one sentence. ## Practical notes Every flag is queryable: `re.flags` returns the flag string in a fixed canonical order, and `re.source` returns the pattern text without delimiters. Flags cannot be changed on an existing regex — the properties are read-only accessors — so to add one you build a new regex from the old one's source or pass the regex itself to `new RegExp(re, "ms")`. A note on `\r\n` input: with `m`, `$` matches before the `\r` and `^` after the `\n`, so a pattern such as `/^\w+$/gm` over Windows-style text leaves a stray `\r` outside the match rather than failing — a classic source of trailing-carriage-return bugs when the results are compared against clean strings. Normalising line endings before matching is usually cheaper than encoding them in the pattern.

  • How would you make a pattern cross newlines without using any flag?
    Replace `.` with a character class that covers everything, such as `[\s\S]` or the JavaScript-specific `[^]`. Both match any character including line terminators. This is what everyone wrote before ES2018, and it is still preferable when only part of the pattern should cross lines while the rest stays line-bounded.
  • What does /^.+$/gms match on a three-line string, and how does dropping the s change it?
    With both flags, `.` crosses lines and `+` is greedy, so it matches the entire string as one match. Drop the `s` and `.` stops at each line terminator, so you get three matches, one per line. The `m` flag alone never lets a single match span lines.
  • Can you turn on a flag for a regex you already have?
    Not in place — `dotAll`, `multiline` and the rest are read-only accessors, and `flags` is derived from them. Build a new object instead: `new RegExp(existing, "ms")` reuses the source with the flags you name. Since ES6 the constructor accepts a RegExp as its first argument for exactly this.

saying these in an interview costs you the question

  • Says the m (multiline) flag makes . match newlines
  • Thinks . already matches every character by default
  • Believes m splits the input into separate lines for matching
  • Assumes s and m are alternatives rather than orthogonal
  • Cannot name any pre-ES2018 way to match across lines

context