How to Split a String by Regex in TypeScript

You split a string by regex in TypeScript by passing a RegExp to split() instead of a string. Every match becomes a cut point, and you get back a string[]:

const parts: string[] = "react,  node;typescript".split(/\s*[,;]\s*/);
// ["react", "node", "typescript"]

Each snippet here was compiled in strict mode with TypeScript 7.0.2 and Node.js v22.22.2. I pasted the console output straight from those runs.

How split() works with a regular expression

The pattern describes the separator, so one regex can eat messy spacing around commas and semicolons. Tabs and runs of spaces collapse with \s+:

const tags: string = "react,  node;typescript ,  vite";
const parts: string[] = tags.split(/\s*[,;]\s*/);
console.log(parts);

const address = "1600   Main St\tSuite 210";
console.log(address.split(/\s+/));
console.log(",Austin,,Denver,".split(/,/));

Output:

[ 'react', 'node', 'typescript', 'vite' ]
[ '1600', 'Main', 'St', 'Suite', '210' ]
[ '', 'Austin', '', 'Denver', '' ]

Leading, trailing and doubled separators leave empty strings, and the regex doesn’t fix that on its own. Clean them up with removing empty strings from an array.

I keep the two everyday cases in their own guides: splitting a string with multiple separators and splitting a string by new line.

Do regex flags work with split()?

Most do, but g doesn’t matter. split() hands the work to the regex’s [Symbol.split] method, which copies the pattern and finds every match anyway:

const re = /and/g;
const items = "Salt and Pepper AND Garlic and Thyme";

console.log("g:   ", items.split(re));
console.log("lastIndex after split:", re.lastIndex);
console.log("g+i: ", items.split(/\s*and\s*/gi));
console.log("i:   ", items.split(/\s*and\s*/i));

Output:

g:    [ 'Salt ', ' Pepper AND Garlic ', ' Thyme' ]
lastIndex after split: 0
g+i:  [ 'Salt', 'Pepper', 'Garlic', 'Thyme' ]
i:    [ 'Salt', 'Pepper', 'Garlic', 'Thyme' ]
Command Prompt output showing TypeScript split with a regex: the g flag changes nothing and lastIndex stays 0
Case-sensitive /and/g skips “AND”; adding i catches it, with or without g.

Because it works on a copy, lastIndex stays at 0, unlike exec() and test() with a shared global regex. For case-insensitive checks without a regex, see comparing strings in TypeScript.

Keep the separators with capture groups

Parentheses in the pattern change the output: whatever a group captures is spliced into the array between the pieces. Use (?:...) for plain grouping:

const formula = "120+35.5-18*2";

console.log("no group:   ", formula.split(/[-+*]/));
console.log("with group: ", JSON.stringify(formula.split(/([-+*])/)));
console.log("non-capture:", formula.split(/(?:[-+*])/));

// Two alternatives, two groups: the group that didn't match gives undefined
const mixed: string[] = "a,b:c".split(/(,)|(:)/);
console.log(mixed);

// limit truncates AFTER splitting, and captured separators count
console.log("limit 3:    ", formula.split(/([-+*])/, 3));

Output:

no group:    [ '120', '35.5', '18', '2' ]
with group:  ["120","+","35.5","-","18","*","2"]
non-capture: [ '120', '35.5', '18', '2' ]
[ 'a', ',', undefined, 'b', undefined, ':', 'c' ]
limit 3:     [ '120', '+', '35.5' ]
Command Prompt output of TypeScript split with a capture group keeping operators, undefined entries and a limit of 3
The operators come back as elements; an unmatched group adds undefined; the limit counts captured items too.

Watch that undefined. TypeScript still types the result as string[], so a later .trim() on it compiles but crashes at runtime. One group with a character class, ([,:]), avoids it.

The second argument, limit, doesn’t stop the search early. It trims the finished array, so /([-+*])/ with 3 returns two numbers and one operator. For the final item, see getting the last element after a split.

Split camelCase and before capital letters

Lookahead (?=...) and lookbehind (?<=...) match a position, not characters. Nothing is removed, so you can cut before a capital letter or after a period and keep both:

console.log("camelCase:".padEnd(14), "shippingAddressLine".split(/(?=[A-Z])/));

// Keep acronyms together: split between lower->Upper and before the last capital of a run
const acronym = /(?<=[a-z0-9])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])/;
console.log("acronyms:".padEnd(14), "parseHTTPResponse2026Code".split(acronym));

const words = "XMLHttpRequest".split(acronym).map((w) => w.toLowerCase()).join("-");
console.log("kebab-case:".padEnd(14), words);

// Lookbehind: split after sentence punctuation, keep the punctuation
const note = "Order shipped. Arrives Oct 21! Need help? Call us.";
console.log("sentences:".padEnd(14), note.split(/(?<=[.!?])\s+/));

Output:

camelCase:     [ 'shipping', 'Address', 'Line' ]
acronyms:      [ 'parse', 'HTTP', 'Response2026', 'Code' ]
kebab-case:    xml-http-request
sentences:     [ 'Order shipped.', 'Arrives Oct 21!', 'Need help?', 'Call us.' ]
Command Prompt output of TypeScript regex split for camelCase words, acronyms, kebab-case and sentences using lookahead and lookbehind
A plain (?=[A-Z]) splits camelCase; the longer pattern keeps HTTP and XML in one piece.

Lookbehind is supported in every current browser and Node.js release. If you only need the text after one marker, getting the string after a character is simpler.

How do I split Unicode and emoji safely?

split("") cuts UTF-16 code units, which breaks emoji in half. An empty regex with the u flag splits by code point instead, and Intl.Segmenter splits by what readers see as one character:

const reaction = "ok👍🏽";

console.log("split(''):    ", reaction.split("").length, "pieces");
console.log("split(/(?:)/u):", reaction.split(/(?:)/u));

const family = "hi👨‍👩‍👧";
const seg = new Intl.Segmenter("en-US", { granularity: "grapheme" });
console.log("graphemes:    ", Array.from(seg.segment(family), (s) => s.segment));

// \p{...} classes need the u flag: split on any punctuation or symbol
console.log("\\p{P}:        ", "Café—Seattle/Boston·2026".split(/[\p{P}\p{S}]+/u));

Output:

split(''):     6 pieces
split(/(?:)/u): [ 'o', 'k', '👍', '🏽' ]
graphemes:     [ 'h', 'i', '👨‍👩‍👧' ]
\p{P}:         [ 'Café', 'Seattle', 'Boston', '2026' ]

Skin tones and family emoji are several code points glued together, so only the segmenter keeps them whole. The same code-unit counting explains surprises in a TypeScript string length check.

Write a custom splitter with Symbol.split

split() accepts any object with a [Symbol.split] method, and TypeScript’s types allow it. It’s a tidy home for a reusable rule:

// Any object with a [Symbol.split] method can be passed to split()
const csvLine = {
  [Symbol.split](value: string): string[] {
    // split on commas that are NOT inside double quotes
    return value.split(/,(?=(?:[^"]*"[^"]*")*[^"]*$)/).map((f) => f.replace(/^"|"$/g, ""));
  },
};

const row = '1042,"Portland, OR",249.99,2026-10-21';
console.log(row.split(csvLine));

Output:

[ '1042', 'Portland, OR', '249.99', '2026-10-21' ]

It suits simple CSV lines; files with escaped quotes need a real parser. Stripping quotes is covered in removing quotes from a string.

Catch broken regex patterns at compile time

TypeScript 5.5 and later check regex literal syntax, so a bad pattern fails the build instead of throwing in production:

const zip = "Dallas TX 75201";

const a = zip.split(/\s+{2}/);
const b = zip.split(/(?<state[A-Z]{2})/);
const c = zip.split(/\s/x);

Output:

regex_broken.ts(3,25): error TS1507: There is nothing available for repetition.
regex_broken.ts(4,30): error TS1005: '>' expected.
regex_broken.ts(5,25): error TS1499: Unknown regular expression flag.
Command Prompt output of tsc errors for an invalid TypeScript split regex: nothing to repeat, missing group name bracket and unknown flag
tsc flags the bad quantifier, the unfinished named group and the unknown x flag.

Patterns built with new RegExp() aren’t checked, so they still throw a SyntaxError at runtime. Wrap dynamic ones in try:

const pattern: string = "\\s+{2}";
try {
  "Dallas TX 75201".split(new RegExp(pattern));
} catch (err) {
  console.log((err as Error).name + ":", (err as Error).message);
}

Output:

SyntaxError: Invalid regular expression: /\s+{2}/: Nothing to repeat

Want more string tutorials? These pick up where this one stops:

Frequently asked questions

How do I split a string by regex in TypeScript?

Pass a regular expression to split(), for example text.split(/\s*,\s*/). It returns a string[].

Does split() need the g flag?

No. Split always finds every match, so g is ignored. Flags like i and u still apply.

Why does my split result contain the separators?

Your pattern has a capturing group. Switch to (?:...) or drop the parentheses to leave separators out.

How do I split a string before each capital letter?

Use a lookahead, str.split(/(?=[A-Z])/). It cuts at the position and keeps every letter.

Where is split() with a RegExp documented?