The m flag: multiline anchors
The m flag makes ^ and $ match at every line boundary instead of only at the two ends of the string. It affects those two anchors and nothing else in the pattern.
At a glance
- Flag letter
- m
- Property
- RegExp.prototype.multiline
- Changes
- the ^ and $ anchors, and nothing else
- Line terminators recognised
- \n, \r, U+2028 and U+2029
- Does not change
- the dot, which still needs the s flag before it crosses a newline
- Usual pairing
- /^...$/gm, to run a pattern once per line
In detail
Under m, ^ matches at index zero and at every position directly after a line terminator, and $ matches at the end of the string and directly before every line terminator. Matching /^./g against "a\nb" gives one result. The same pattern with m gives two, one per line, which is the whole point of the flag.
JavaScript counts a bare carriage return as a line terminator for these anchors, alongside the line feed and the two Unicode paragraph separators. So /^\w+$/gm against "a\r\nb" returns the two letters with the \r sitting between the matches rather than trapped inside one. Engines that only break on \n behave differently, which is where a ported pattern starts finding more lines than it used to.
The most common mistake is reaching for m when the pattern needs to span lines. The two jobs are separate: m only touches the anchors, and only the s flag lets the dot cross a newline. A pattern that stops at the end of the first line does not want m, it wants s or a [\s\S] class in the one place that has to travel.
Because both anchors are zero width, m plus g turns a replace into a per line insert. Replacing /^/gm with a quote marker prefixes every line without touching the content, and replacing /$/gm appends to every line. That is genuinely useful and also the reason /^$/gm finds blank lines but deletes nothing when you replace it with an empty string.
Examples
"a\nb".match(/^./g); // ["a"]
"a\nb".match(/^./gm); // ["a", "b"]
"a\nb".replace(/^/gm, "> "); // "> a\n> b"// JavaScript treats a bare \r as a line terminator for ^ and $.
"a\r\nb".match(/^\w+$/gm); // ["a", "b"]
// m does nothing at all for the dot.
"a\nb".match(/a.b/); // null
"a\nb".match(/a.b/s)[0]; // "a\nb"Things that catch people out
- The m flag does not make the dot match a newline. That is the s flag, and reaching for m instead is the usual reason a pattern silently stops at the end of the first line.
- Because JavaScript breaks lines on a bare \r as well as \n, a pattern ported from an engine that only breaks on \n finds extra line starts here on Windows text.
- ^ and $ are zero width. A global replace against /^$/gm inserts the replacement into every blank line rather than removing the line.
- Turning on m changes every ^ and $ in the pattern, including any inside a lookaround. If only one position needs line semantics, use a lookaround assertion instead.
Try it: Regex Tester
Type a pattern and a test string, watch every match highlight as you go, read a plain English breakdown of each token, and check the numbered and named groups it captured.
Regex Tester
LOCAL · Browser-OnlyTest and debug regular expressions with live matching and highlights
Related Regex syntax
All regex syntax →The ^ anchor: start of string, or start of line
Where the caret matches with and without the m flag, why it consumes nothing, and the entirely different job it does inside a character class.
The $ anchor: end of string, and the trailing newline question
Where $ matches in JavaScript, why it refuses to match before a trailing newline when Python and Ruby allow it, and how to write a literal dollar sign.
The s flag: dotAll mode
What dotAll changes, why it has no effect at all on character classes, and the [\s\S] idiom that did the same job before the s flag existed.
The u flag: Unicode mode
What the u flag changes about how a pattern is parsed, the escape forms it unlocks, and the previously tolerated syntax it turns into a hard error.
The y flag: sticky matching at lastIndex
How the sticky flag anchors a match to the current lastIndex, why it is the right tool for a hand written tokenizer, and how it behaves alongside g.
The d flag: capture group start and end offsets
What the d flag adds to a match result, how indices.groups mirrors named captures, and when the extra offsets are worth asking the engine for.
Tools for this
- Regex Tester Free online regex tester - test and debug regular expressions with live matching and highlights
- Regex Playground Regex playground with live match highlighting, named groups, find and replace, and shareable links