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"

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.

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

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

\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
| Approach | Best for | Watch out for |
|---|---|---|
Template literal `...` | Most multiline text, with ${...} values | Source indentation becomes part of the text |
"\n" escapes | Short strings on one source line | Hard to read beyond two or three lines |
lines.join("\n") | Line counts that depend on data | Remember the separator argument |
dedent tag | Indented templates inside functions | Values with their own line breaks |
String.raw | Paths, 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:
- Compare strings in TypeScript
- Convert a string to a number in TypeScript
- Remove empty strings from an array in TypeScript
- Filter arrays in TypeScript
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?
See Template literals (Template strings) on MDN.

Bijay Kumar is a 13-time Microsoft MVP with more than 18 years in software development, and the founder of Python Guides and TSinfo Technologies. He started out building .NET and SharePoint solutions at HP, TCS and KPIT before moving into Python, machine learning and AI, and he also builds web apps with TypeScript and React. He writes the tutorials here himself, and every example is run before publishing so you see the real output. More about Bijay · Microsoft MVP profile · LinkedIn