Regex for Multiline Text Matching — The m and s Flags Explained
By default, JavaScript regex treats a string as one continuous line: ^ and $ anchor to the very start and end, and . never matches a newline. When you're parsing multi-line log files, config blocks, or markdown, you need the m and s flags to change that behavior — and it's easy to reach for the wrong one.
Default Behavior (No Flags)
const text = 'line one\nline two\nline three'; // Without 'm', ^ and $ only match the start/end of the WHOLE string text.match(/^line/g); // ['line'] — only matches "line one" at the very start text.match(/^line/gm); // ['line', 'line', 'line'] — matches every line's start
The m (Multiline) Flag
With m, ^ matches right after every newline, and $ matches right before every newline — effectively per-line anchors instead of whole-string anchors.
const log = `INFO: server started ERROR: connection failed INFO: retrying ERROR: timeout`; const errorLines = log.match(/^ERROR:.*$/gm); console.log(errorLines); // ['ERROR: connection failed', 'ERROR: timeout']
The s (Dotall) Flag
By default, . matches "any character except line terminators." The s flag makes it match literally any character, including \n — essential when you need to capture a block that spans multiple lines.
const config = `START key1=value1 key2=value2 END`; // Without 's', .* cannot cross newlines, so this fails to match the whole block config.match(/START(.*)END/); // null // With 's', .* happily crosses newlines config.match(/START(.*)END/s)[1]; // '\nkey1=value1\nkey2=value2\n'
Combining m and s
These flags solve different problems and are often used together — for example, extracting a multi-line block that itself starts with a line-anchored marker:
const doc = `--- title: My Post date: 2026-07-11 --- Body content goes here. More body text.`; // Extract YAML frontmatter: starts at line-start '---', ends at line-start '---' const frontmatter = doc.match(/^---$(.*?)^---$/ms); console.log(frontmatter[1]); // '\ntitle: My Post\ndate: 2026-07-11\n'
Here m lets ^---$ match the delimiter lines anywhere in the document, while s lets .*? (lazy) cross the newlines between them.
Practical Use Cases
- Log parsing — extract every line matching a pattern with
m - Config/frontmatter extraction — grab a block between two markers with
s - Comment stripping — match
/\/\*.*?\*\//sto remove multi-line/* ... */comments - Markdown code fences — match content between ```` ``` ```` markers spanning several lines
Frequently Asked Questions
The m (multiline) flag changes ^ and $ to match the start and end of each line within the string, instead of only the start and end of the entire string.
The s (dotall) flag makes the dot (.) match newline characters as well as every other character. Without it, . matches any character except line terminators.
Yes. They control different things — m affects ^ and $ anchors, while s affects the dot (.) — and are commonly combined, e.g. /pattern/ms, when parsing multi-line log entries or blocks of text.