How to Split a String by New Line in TypeScript

To split a string by new line in TypeScript, call split(/\r?\n/). The regular expression matches both Unix line breaks (\n) and Windows line breaks (\r\n), and you get back a string[] with one entry per line:

const lines: string[] = text.split(/\r?\n/);

Nothing here is made up: tsc compiled each file in strict mode (TypeScript 7.0.2 and Node.js v22.22.2), Node ran the output, and I copied what the console printed.

You’ll also see why split(" ") falls short, how you drop blank lines, how you split a file, and which compiler errors you may hit.

Split a string by new line with split(/\r?\n/)

Here’s text with Windows line endings, like a file saved in Notepad. Compare a plain " " split with the regex:

const fromWindows: string = "Austin, TX\r\nDenver, CO\r\nSeattle, WA";

const naive = fromWindows.split("\n");
console.log("split(\"\\n\"):", naive);

const lines: string[] = fromWindows.split(/\r?\n/);
console.log("split(/\\r?\\n/):", lines);

console.log("naive[0] is clean:", naive[0] === "Austin, TX");
console.log("lines[0] is clean:", lines[0] === "Austin, TX");

Output:

split("\n"): [ 'Austin, TX\r', 'Denver, CO\r', 'Seattle, WA' ]
split(/\r?\n/): [ 'Austin, TX', 'Denver, CO', 'Seattle, WA' ]
naive[0] is clean: false
lines[0] is clean: true
Command Prompt output of TypeScript splitting a CRLF string with split of backslash n versus the regex backslash r optional backslash n
With "\n", every line but the last keeps a hidden \r; the regex gives clean values.

Those leftover characters don’t show when you print one line, yet === comparisons fail. For other separators, such as commas, see converting a string to an array in TypeScript.

Why does split(“\n”) leave \r at the end of each line?

Windows ends lines with carriage return plus line feed ( ); Linux and macOS use only . Splitting on leaves the behind.

In /\r?\n/, the ? makes the \r optional, so one pattern covers both. Very old Mac files used a lone \r; if you might see those, use /\r\n|\r|\n/:

const mixed: string = "Line 1\nLine 2\r\nLine 3\rLine 4";

console.log(mixed.split(/\r?\n/));
console.log(mixed.split(/\r\n|\r|\n/));

Output:

[ 'Line 1', 'Line 2', 'Line 3\rLine 4' ]
[ 'Line 1', 'Line 2', 'Line 3', 'Line 4' ]

Line breaks typed inside a template literal are always stored as , as I show in creating a multiline string in TypeScript, so this mostly matters for outside text.

Split by new line and remove empty lines

Text pasted into a <textarea> often has blank lines, stray spaces and a trailing newline:

const pasted: string = "  Chicago, IL\n\nPhoenix, AZ  \r\n\r\nBoston, MA\n";

console.log("raw:      ", pasted.split(/\r?\n/));
console.log("collapsed:", pasted.split(/[\r\n]+/));

const clean = pasted
  .split(/\r?\n/)
  .map((line) => line.trim())
  .filter((line) => line !== "");
console.log("clean:    ", clean);

Output:

raw:       [ '  Chicago, IL', '', 'Phoenix, AZ  ', '', 'Boston, MA', '' ]
collapsed: [ '  Chicago, IL', 'Phoenix, AZ  ', 'Boston, MA', '' ]
clean:     [ 'Chicago, IL', 'Phoenix, AZ', 'Boston, MA' ]
Command Prompt output of TypeScript splitting pasted text by new line, collapsing blank lines and trimming with map and filter
The raw split keeps empty strings, /[\r\n]+/ still leaves spaces and a trailing "", and trim plus filter cleans it.

A trailing newline always leaves a final empty string. I’d go with trim plus filter; more patterns are in filtering arrays in TypeScript.

Turn each line into a typed object

Splitting is usually step one. For a small CSV report, peel off the header, then split each row on commas:

interface Order {
  city: string;
  state: string;
  total: number;
}

const report: string = `city,state,total
Austin,TX,1249.5
Denver,CO,980.25
Seattle,WA,1520
`;

const [header, ...rows] = report.trim().split(/\r?\n/);
console.log("columns:", header.split(","));

const orders: Order[] = rows.map((row) => {
  const [city, state, total] = row.split(",");
  return { city, state, total: Number(total) };
});

for (const o of orders) {
  console.log(`${o.city}, ${o.state}: $${o.total.toFixed(2)}`);
}

Output:

columns: [ 'city', 'state', 'total' ]
Austin, TX: $1249.50
Denver, CO: $980.25
Seattle, WA: $1520.00

The Order interface catches typos like o.totl at compile time. For tougher number parsing, see converting a string to a double in TypeScript.

Split a text file by new line in Node.js

For a small file, use readFileSync and split. For a big log, stream it with readline instead:

import { createReadStream, readFileSync, writeFileSync } from "node:fs";
import { createInterface } from "node:readline";

writeFileSync("stores.txt", "Miami, FL\r\nPortland, OR\r\nNashville, TN\r\n");

// Small file: read it all, then split
const lines = readFileSync("stores.txt", "utf8").split(/\r?\n/).filter(Boolean);
console.log(lines.length, "lines:", lines);

// Large file: stream it one line at a time
async function main(): Promise<void> {
  const rl = createInterface({ input: createReadStream("stores.txt"), crlfDelay: Infinity });
  let n = 0;
  for await (const line of rl) {
    console.log(`readline ${++n}:`, JSON.stringify(line));
  }
}
main();

Output:

3 lines: [ 'Miami, FL', 'Portland, OR', 'Nashville, TN' ]
readline 1: "Miami, FL"
readline 2: "Portland, OR"
readline 3: "Nashville, TN"
Command Prompt output of a TypeScript Node.js script splitting a CRLF text file by new line and reading it with readline
Both approaches return three clean store names; crlfDelay: Infinity treats \r\n as one break.

TypeScript 7 doesn’t load @types/node automatically, so I compiled with --types node. Once you have the array, any loop from looping through arrays in TypeScript works.

Fix TypeScript errors after splitting a string into lines

Two common mistakes: storing the result in a string, and using a line unchecked when noUncheckedIndexedAccess is on:

const notes: string = "Call Dallas office\nSend Q4 invoice";

const lines: string = notes.split(/\r?\n/);

const first = notes.split(/\r?\n/)[0];
console.log(first.toUpperCase());

Output:

nl_errors.ts(3,7): error TS2322: Type 'string[]' is not assignable to type 'string'.
nl_errors.ts(6,13): error TS18048: 'first' is possibly 'undefined'.
Command Prompt showing TypeScript errors TS2322 string array not assignable to string and TS18048 possibly undefined after split
split() always returns string[], and with noUncheckedIndexedAccess each index is string | undefined.

Type the variable as string[], and check a line with if (first !== undefined) before calling methods on it.

Need just the final line? You can use lines.at(-1) with optional chaining; see getting the last element after a split in TypeScript.

Ways to split a string by new line compared

PatternHandlesUse it when
split("\n")Unix line breaks onlyYou created the text yourself in code
split(/\r?\n/)Windows and UnixMost input: files, uploads, APIs
split(/\r\n|\r|\n/)Windows, Unix, old MacUnknown or legacy sources
split(/[\r\n]+/)Any break, blank lines mergedBlank lines don’t matter
readline + crlfDelayLarge files, streamedFiles too big to load at once

These guides pick up where this one stops:

Frequently asked questions

How do I split a string by new line in TypeScript?

Use text.split(/\r?\n/). It returns a string[] and handles both \n and \r\n line endings.

Why is the last element of my split an empty string?

The text ends with a newline, so there’s an empty piece after it. Call trim() before splitting or add .filter(Boolean).

How do I split by new line and skip blank lines?

Chain .map((line) => line.trim()).filter((line) => line !== "") after the split, or split on /[\r\n]+/ if spaces don’t matter.

Is there a splitlines() method in TypeScript?

No. Strings have no built-in splitlines() like Python; split(/\r?\n/) is the standard equivalent.

How do I join the lines back into one string?

Call lines.join("\n"), or join("\r\n") if the output needs Windows line endings.

Where is String.prototype.split() documented?