RegExp.prototype.source
Returns the pattern text of a regular expression without the enclosing slashes or flags.
get RegExp.prototype.source- Added in JavaScript ES3 (1999)
Return value
stringThe source text of the pattern, escaped so that wrapping it in forward slashes yields a valid regex literal. An empty pattern returns "(?:)".
Errors
TypeError if accessed on a value that is not a RegExp object (other than RegExp.prototype itself).
Changelog
- PHP ES5The escaping of forward slashes and line terminators, and the "(?:)" result for an empty pattern, were standardised; earlier engines disagreed.
- PHP ES6source became an accessor on RegExp.prototype rather than an own data property of each instance, so RegExp.prototype.source itself returns "(?:)".
source is the pattern with the delimiters stripped: /^\d+$/gi.source is "^\d+$". It is what you need to take a regex apart and put it back together — the flags live on flags, and String(re) gives you the whole literal including both.
Composing patterns
The main use is building a large regex from small named pieces. Keep each fragment as its own literal so your editor still syntax-highlights it, then join the source strings inside new RegExp(). Wrap fragments containing alternation in (?:...) before concatenating, or a|b will bind across the seam and match something you never intended.
The escaping is deliberate
Forward slashes come back as \/ and raw newlines as \n, so "/" + re.source + "/" is always a syntactically valid literal. An empty pattern reports "(?:)" for the same reason — // would start a comment.
Round-tripping through new RegExp(re.source, re.flags) reproduces the regex exactly, except that the copy starts with lastIndex at 0.
Example
When to use it
- Assemble a long URL or log-line pattern from small, individually testable fragments
- Store a regex in JSON by saving source and flags separately
- Clone a regex with different flags without hardcoding the pattern twice
Frequently asked questions
How do I combine two regular expressions in JavaScript?
Concatenate their source strings inside new RegExp(): new RegExp(a.source + b.source, flags). Wrap each fragment in a non-capturing group (?:...) first if it contains a top-level alternation, otherwise the | will span the join.
Why does an empty regex have source "(?:)"?
Because the value must remain a valid pattern when wrapped in slashes, and // would be parsed as a line comment. "(?:)" is an empty non-capturing group, which matches the empty string just like // does.
How is source different from toString()?
source is the pattern alone; toString() (and String(re)) returns the complete literal including the slashes and the flags. Use source when you are rebuilding a pattern, toString() when you are displaying or logging one.
More examples
Building one pattern from named fragments
Each fragment stays a real regex literal you can test on its own, and joining their source strings produces the full anchored pattern. Fragments with a top-level | would need their own (?:...) wrapper first.
const scheme = /https?:\/\//;
const host = /[\w.-]+/;
const path = /\/\S*/;
const url = new RegExp(
"^" + [scheme, host, path].map(p => p.source).join("") + "$"
);
console.log(url.source);
console.log(url.test("https://example.com/a/b")); // true
console.log(url.test("ftp://example.com/a")); // falseRound-tripping a regex
source plus flags fully reconstructs the pattern, which is how you persist a regex to JSON. The one thing that does not survive is lastIndex, which is usually what you want.
const original = /a\/b/gi;
original.lastIndex = 3;
const copy = new RegExp(original.source, original.flags);
console.log(copy.source === original.source); // true
console.log(String(copy) === String(original)); // true
console.log(copy.lastIndex); // 0 - state is not copied
console.log(original.lastIndex); // 3
Discussion