How to Create a Multiline String in TypeScript (With Examples)

The easiest way to create a multiline string in TypeScript is a template literal: wrap the text in backticks instead of quotes and press Enter wherever you want a new line. Line breaks stay as typed, and ${...} adds values:

const note = `Line one
Line two
Total: $${total.toFixed(2)}`;

Every snippet in this guide was compiled in strict mode and run with TypeScript 7.0.2 and Node.js v22.22.2. The outputs you’ll see are pasted from those runs, not typed by hand.

I’ll cover template literals, the older \n and join() approaches, stripping unwanted indentation, line endings, String.raw, and multiline text in React TSX.

Create a multiline string in TypeScript with template literals

Template literals arrived in ES2015, and TypeScript supports them with any compile target. Here’s an order confirmation email built in one string, with the customer name, order number and total filled in:

const customer: string = "Emily Carter";
const orderId: number = 10482;
const total: number = 86.4;
const shipDate: string = "October 16, 2026";

const email = `Hi ${customer},

Thanks for your order #${orderId} from our Denver store.
Order total: $${total.toFixed(2)}
Estimated ship date: ${shipDate}

- The Mountain Supply Team`;

console.log(email);
console.log("---");
console.log("lines:", email.split("\n").length);
console.log(JSON.stringify(email.slice(0, 22)));

Output:

Hi Emily Carter,

Thanks for your order #10482 from our Denver store.
Order total: $86.40
Estimated ship date: October 16, 2026

- The Mountain Supply Team
---
lines: 7
"Hi Emily Carter,\n\nThan"
Command Prompt output of a TypeScript multiline string built with a template literal for an order email
The email prints on separate lines, and JSON.stringify shows the real \n characters inside.

Notice the $${total.toFixed(2)} part. The first $ is a plain dollar sign, and the ${...} after it is the placeholder. TypeScript still checks the types of every expression inside the placeholders.

email is just a string, so everything you already do with strings still works. You can measure it the way I show in checking string length in TypeScript, or search and replace inside it.

Why does a normal quoted string fail on multiple lines?

Single and double quotes can’t contain a raw line break. If you press Enter inside a "..." string, the compiler stops right there:

const address: string = "1600 Market Street
Philadelphia, PA 19103";

Output (tsc):

ml_quoted.ts(1,44): error TS1002: Unterminated string literal.
ml_quoted.ts(2,18): error TS1005: ';' expected.
ml_quoted.ts(2,23): error TS1005: ';' expected.
ml_quoted.ts(2,25): error TS1002: Unterminated string literal.
Command Prompt showing the TypeScript compiler error Unterminated string literal for a double-quoted string split across two lines
tsc reports an unterminated string literal on the first line of the address.

That’s why older code uses escape sequences or the + operator. Both still work, and you’ll meet them in existing projects. A backslash at the very end of a line is different: it continues the same line instead of adding a break.

// 1. \n escape sequences in a normal string
const a: string = "Austin, TX\nDallas, TX\nHouston, TX";

// 2. Concatenation across source lines
const b: string = "Austin, TX\n" +
  "Dallas, TX\n" +
  "Houston, TX";

// 3. A backslash at the end of a line continues the SAME line
const c: string = `Austin, TX, Dallas, TX, \
Houston, TX`;

console.log(a);
console.log("a === b:", a === b);
console.log(c);

Output:

Austin, TX
Dallas, TX
Houston, TX
a === b: true
Austin, TX, Dallas, TX, Houston, TX

Strings a and b are identical; c stays on one line. I’d only reach for + when the pieces come from different places, which I cover in string concatenation in TypeScript.

Build a multiline string from an array with join()

When the number of lines depends on your data, collect them in an array and join them with "\n" at the end. It’s cleaner than appending inside a loop, and you never end up with a stray newline at the start or end:

interface Item {
  sku: string;
  name: string;
  price: number;
}

const items: Item[] = [
  { sku: "TX-104", name: "Trail Backpack", price: 74.99 },
  { sku: "TX-221", name: "Water Bottle", price: 18.5 },
  { sku: "TX-307", name: "Headlamp", price: 32 },
];

const lines: string[] = ["Order summary - Austin, TX"];
for (const item of items) {
  lines.push(`${item.sku}  ${item.name.padEnd(16)} ${("$" + item.price.toFixed(2)).padStart(7)}`);
}
const sum = items.reduce((s, i) => s + i.price, 0);
lines.push(`Total: $${sum.toFixed(2)}`);

console.log(lines.join("\n"));

Output:

Order summary - Austin, TX
TX-104  Trail Backpack    $74.99
TX-221  Water Bottle      $18.50
TX-307  Headlamp          $32.00
Total: $125.49

I used padEnd() and padStart() to line up the columns. The same padding trick is behind adding leading zeros to numbers in TypeScript, and the loop itself is a plain for…of loop in TypeScript.

You could also write items.map(...).join("\n") in one line. Pick whichever version your team reads faster.

Typing the array with an interface also means a typo like item.prise fails at compile time instead of printing undefined in the middle of your text. I go deeper on that pattern in TypeScript interfaces for arrays of objects.

How do you remove indentation from a multiline string?

This one’s the most common surprise. A template literal keeps every space you type, so if you indent it to match the surrounding code, that indentation ends up in your text, along with an empty first and last line:

function welcome(name: string): string {
  if (name) {
    return `
      Welcome, ${name}!
        Plan: Pro (monthly)
      Renews: November 1, 2026
    `;
  }
  return "";
}
console.log(JSON.stringify(welcome("Jordan")));

function dedent(strings: TemplateStringsArray, ...values: unknown[]): string {
  let text = strings[0];
  values.forEach((v, i) => (text += String(v) + strings[i + 1]));
  const lines = text.replace(/^\n/, "").replace(/\n[ \t]*$/, "").split("\n");
  const indents = lines.filter((l) => l.trim()).map((l) => l.length - l.trimStart().length);
  const cut = Math.min(...indents);
  return lines.map((l) => l.slice(cut)).join("\n");
}

function welcome2(name: string): string {
  return dedent`
    Welcome, ${name}!
      Plan: Pro (monthly)
    Renews: November 1, 2026
  `;
}
console.log(welcome2("Jordan"));

Output:

"\n      Welcome, Jordan!\n        Plan: Pro (monthly)\n      Renews: November 1, 2026\n    "
Welcome, Jordan!
  Plan: Pro (monthly)
Renews: November 1, 2026
Command Prompt output of a TypeScript template literal with unwanted leading spaces, then the same text cleaned by a dedent tag function
The first string carries extra spaces and newlines; the dedent tag removes the common indentation and keeps the inner nesting.

The dedent function is a tagged template. TypeScript passes the literal parts as a TemplateStringsArray and the placeholder values as the rest of the arguments. The function rebuilds the text, drops the first and last empty lines, and removes the smallest shared indent.

The plan line keeps its two extra spaces because only the common indent is cut. If a placeholder value contains its own line breaks, its lines count too, so keep that in mind for nested templates.

Don’t need a helper? Write the text flush against the left margin, or call .trim() to drop just the leading and trailing whitespace. For other cleanup jobs, see removing spaces from a string in TypeScript.

Line endings and splitting a multiline string into lines

The demo files were saved with Windows \r\n line endings, yet a template literal never contains \r. JavaScript normalizes every line break inside backticks to \n, so your output is the same on Windows, macOS and Linux.

Text that comes from outside isn’t so tidy. A file read from disk, a <textarea> or an API response may use \r\n, so split with a regular expression:

const notes = `Seattle, WA
Portland, OR
Boise, ID`;
console.log("contains \\r:", notes.includes("\r"));

// Text from a Windows file or a textarea may use \r\n
const fromFile: string = "Seattle, WA\r\nPortland, OR\r\n\r\nBoise, ID\r\n";

console.log(fromFile.split("\n"));
const cities = fromFile.split(/\r?\n/).filter((line) => line.trim() !== "");
console.log(cities);
console.log("count:", cities.length);

Output:

contains \r: false
[ 'Seattle, WA\r', 'Portland, OR\r', '\r', 'Boise, ID\r', '' ]
[ 'Seattle, WA', 'Portland, OR', 'Boise, ID' ]
count: 3
Command Prompt output of splitting a TypeScript multiline string with CRLF line endings into a clean array of lines
Splitting on \n leaves \r on each line; /\r?\n/ plus a filter gives a clean list.

The filter drops blank lines. If you only want to know whether the whole string is empty after trimming, see checking if a string is empty in TypeScript.

Going the other way is just as simple. To swap every line break for a comma or a space, use text.replaceAll(/\r?\n/g, ", "), as covered in replacing all occurrences of a substring in TypeScript.

In Node.js, os.EOL gives the platform line ending, which is \r\n on Windows. I only use it when writing files for Windows tools; for anything shown in a browser or sent to an API, plain \n is the safer choice.

Keep backslashes with String.raw

Inside a normal template literal, backslashes start escape sequences, so a Windows path needs doubled backslashes. Tag the template with String.raw and every backslash is kept as typed, while line breaks still work:

const cooked = `Folder: C:\\Reports\\2026
Tab:\tdone`;
const raw = String.raw`Folder: C:\Reports\2026
Tab:\tdone`;
console.log(cooked);
console.log(raw);

const tick = `Run \`npm test\` before you push.`;
console.log(tick);

Output:

Folder: C:\Reports\2026
Tab:	done
Folder: C:\Reports\2026
Tab:\tdone
Run `npm test` before you push.

The raw version prints \t literally instead of a tab. To put a backtick inside a template literal, escape it with a backslash, as the last line shows.

Multiline strings in React TSX

In a .tsx file, a template literal works the same way, but HTML collapses line breaks into a single space when it renders. Add the CSS rule white-space: pre-line to keep them visible. This component passed a strict type-check in a Vite React project:

type Props = { street: string; city: string; state: string; zip: string };

export function ShippingLabel({ street, city, state, zip }: Props) {
  const label = `${street}
${city}, ${state} ${zip}`;

  return <p style={{ whiteSpace: "pre-line" }}>{label}</p>;
}

Use pre-wrap instead of pre-line when you also want to keep runs of spaces. For long paragraphs of JSX text, separate elements usually beat one big string.

Multiline string options in TypeScript compared

ApproachBest forWatch out for
Template literal `...`Most multiline text, with ${...} valuesSource indentation becomes part of the text
"\n" escapesShort strings on one source lineHard to read beyond two or three lines
lines.join("\n")Line counts that depend on dataRemember the separator argument
dedent tagIndented templates inside functionsValues with their own line breaks
String.rawPaths, regex and other backslash text\n is no longer a newline

If you work with strings a lot, these guides go well with this one:

Frequently asked questions

How do I write a multiline string in TypeScript?

Wrap the text in backticks to make a template literal and press Enter where you want each new line. The line breaks are stored as \n characters.

Can I use single or double quotes for a multiline string?

Not with a raw line break; the compiler reports an unterminated string literal. Use \n escapes inside the quotes, or switch to backticks.

Why does my multiline string have extra spaces?

Template literals keep the indentation you type. Write the text flush left, call .trim(), or use a small dedent tag function.

How do I split a multiline string into an array of lines?

Use text.split(/\r?\n/), which handles both Windows and Unix line endings, then filter out blank lines if you don’t need them.

Does TypeScript need a special setting for template literals?

No. Template literals are standard JavaScript since ES2015, and they compile without any extra tsconfig.json option or library.

Where are template literals documented?