By default, a regular expression treats your input as one long ribbon of text. It doesn’t care that the string happens to contain line breaks. Multiline mode changes that: it teaches two specific tokens to notice where each line begins and ends.
You turn it on with the flag pattern:m.
The flag is narrow in scope. It touches exactly two things: the caret pattern:^ and the dollar pattern:$. Everything else in the pattern behaves the same whether pattern:m is present or not.
without m — anchors only at string ends
^line one\nline two\nline three$
with m — anchors at every line boundary
^line one$^line two$^line three$
Without m, ^ and $ pin to the whole string; with m, they also snap to every line boundary.
Searching at line start ^
Picture a leaderboard with one entry per line. You want the ranking digit that opens each row. The pattern pattern:/^\d/gm walks every line and grabs the digit sitting at its start:
Drop the pattern:m and the caret loses its per-line vision. It reverts to matching only at the very beginning of the whole string, so you get a single hit:
The pattern:g flag is still doing its job here (it asks for all matches), but there’s only one position where pattern:^ succeeds without pattern:m: the top of the text. The subject:2 and subject:3 sit after line breaks, not at the string’s start, so the caret walks right past them.
/^\d/g
▸1st place: Maya
2nd place: Raj
3rd place: Lena
/^\d/gm
▸1st place: Maya
▸2nd place: Raj
▸3rd place: Lena
Where ^ is allowed to match, for /^\d/g versus /^\d/gm.
Toggle the pattern:m flag below and watch the pattern pattern:/^\d/g switch between finding a single digit at the very top and finding the opening digit of every line. Edit the text too — add or remove lines and see where the caret is allowed to land.
Without pattern:m, the dollar only recognizes the end of the entire text. You’d get just the last digit, subject:3, because that’s the only digit sitting at the true end of the string.
Searching for \n instead of ^ $
There’s a second way to work with line breaks: match the newline character \n directly, as an ordinary character in your pattern. This is different from using an anchor, and the difference matters.
Watch what happens when you search for pattern:\d\n instead of pattern:\d$:
Two matches, not three. The subject:3 on the last line has no newline after it, so pattern:\d\n finds nothing there. The final line ends at the string boundary, which an anchor like pattern:$ would happily accept, but a literal \n demands an actual newline character be present.
There’s a second, quieter difference. Each match now carries a match:\n inside it. An anchor is a zero-width test: pattern:^ and pattern:$ check a condition about position and consume nothing. The character \n is real content, so when it matches, it lands in the result string.
/\d$/gm — $ is a zero-width boundary
Maya: 1$↵ Raj: 2$↵ Lena: 3$
result: 1, 2, 3 — three matches, no newline inside
/\d\n/g — \n is consumed as a character
Maya: 1↵ Raj: 2↵ Lena: 3no ↵ here
result: 1\n, 2\n — two matches, each holds a newline
Anchors mark a boundary and consume nothing; \n is a character that gets pulled into the match.
That gives you a clean rule of thumb. Reach for \n in the pattern when you actually want the newline characters to end up in your results. Reach for the anchors pattern:^ and pattern:$ when you just want to locate something at the start or end of a line without pulling the line break along with it.
Run both patterns side by side on the same text. The results are shown with JSON.stringify, so every captured newline appears as a visible match:\n. Notice that pattern:\d$ finds the final digit while pattern:\d\n skips it, and only the \n version drags a newline into each match.