Template literals & tagged templates
Build strings with expressions, preserve line breaks, process templates with tag functions, and keep backslashes with String.raw.
- 01Fill strings with expressionsUse ${} for values, math, function calls, ternaries, and nested templates.
- 02Control text shapePredict when newlines, indentation, escapes, and raw backslashes are preserved.
- 03Write simple tagsReceive template pieces and values separately, then format or escape the values.
Strings that can think before they print
A template literal is a string written with backtick characters instead of quote characters. Backticks unlock three powers: blanks you can fill with expressions, real multi-line text, and a way to send the template through a function before the final string exists.
You already know the basics from the Strings lesson: a backtick string can contain a blank like ${name}. This lesson turns that feature into a reliable tool. We will predict exactly what values become strings, where whitespace comes from, how tags receive their arguments, and why String.raw keeps backslashes untouched.
Imagine a form that says “Hello, ____.” The printed part stays the same; the blank changes for each person. Template literals work the same way, except each blank is real JavaScript that runs before the final string is assembled.
- In real life: The printed words on the form
- In JavaScript: The literal text between backticks
- In real life: Each blank on the form
- In JavaScript: One expression inside a placeholder
- In real life: The filled-in final document
- In JavaScript: The string value JavaScript produces
Where the analogy stops: A paper form usually accepts simple handwriting. A JavaScript placeholder can run any expression: math, function calls, ternaries, or another template.
A template literal is a backtick-delimited string. A placeholder evaluates the expression inside ${...}, converts the result to a string, and inserts it at that spot.
Backticks clash with this site’s inline-code marker, so longer examples appear in code blocks. When you copy them, copy the backtick characters from the code block exactly.
Interpolation: each blank is an expression
STEP THROUGHInterpolation means “put a value into a larger piece of text.” In a template literal, JavaScript runs each expression inside ${...} from left to right. The expression can be as small as a variable or as rich as a ternary decision.
Choose a preset, predict the final sentence, then step through how each ${} blank is evaluated.
script
const items = ["tea","cake"];const paid = true;const line = `${name} ordered ${items.length} ${items.length === 1 ? "item" : "items"}: ${items.join(", ")}`;const status = `${line} — ${paid ? "paid" : "needs payment"}`;console.log(status);Notice that line 4 does not merely paste variables. It counts an array, chooses singular or plural, and calls join. Template literals are readable because the code sits where the result belongs. That makes them perfect for receipts, labels, logs, and messages.
const obj = { name: "Ada" };const custom = { toString() { return "custom object"; } };console.log(`${obj}`);console.log(`${custom}`);console.log(`${["a", "b", "c"]}`);The placeholder conversion uses the same “string” hint you met in the Object to primitive conversion lesson. A plain object becomes [object Object] unless it customizes toString. An array becomes a comma-joined list. If you want a better result, call the method you mean, such as items.join(", ") or JSON.stringify(data).
A placeholder can contain another template literal. Keep nesting shallow, though. If a string starts looking like a puzzle, build a named variable first and interpolate that name.
Multi-line strings preserve the page shape
STEP THROUGHQuote-delimited strings can include a newline by writing \n. Template literals can include the real newline itself. That is wonderful for messages, SQL-like text, small snippets, and test fixtures. It also means accidental indentation is real output.
Step through a multi-line string and watch why indentation appears in the output.
script
Ship date: Friday Bring snacks`;const cleaned = messy .split("\n") .map((line) => line.trim()) .join("\n");console.log(messy);console.log("---");console.log(cleaned);The trim-and-join fix in the experiment is intentionally simple: it removes spaces from both ends of every line. In production you may want a small dedent helper that removes only the shared indentation, so intentional spaces inside the message survive.
| Feature | Normal quoted string | Template literal |
|---|---|---|
| Delimiter | Single or double quotes | Backticks |
| Interpolation | No; placeholders print literally | Yes, placeholders evaluate expressions |
| Line breaks | Use an escape like newline | Real source newlines are allowed |
| Indentation | Only what you type in escapes or concatenated pieces | Every space between the backticks is preserved |
Tagged templates: send the form through a clerk
INTERACTIVEA tagged template puts a function name before the template. JavaScript does not build the final string first. Instead it calls the tag function with two kinds of information: the literal string pieces and the evaluated placeholder values.
The form is not final until the clerk reviews it. The clerk can mark important values, format money, translate text, or escape risky user input before handing back a result.
- In real life: The clerk sees the printed form
- In JavaScript: The strings array
- In real life: The clerk sees the filled-in blanks
- In JavaScript: The rest values
- In real life: The clerk stamps or rewrites the document
- In JavaScript: The tag returns any value it wants
Where the analogy stops: A real clerk usually returns a document. A tag can return any JavaScript value, although strings are the common case.
function highlight(strings, ...values) { console.log(strings.length); console.log(values.length); console.log(strings.raw[0]); return strings.reduce((text, part, index) => text + part + (index < values.length ? "[" + values[index] + "]" : ""), "");}const price = 12;const qty = 3;console.log(highlight`Total: ${price} for ${qty} items`);A tag is just a function call with special arguments. Step through the call shape.
script
function highlight(strings, ...values) { console.log(strings.length); console.log(values.length); console.log(strings.raw[0]); return strings.reduce((text, part, index) => text + part + (index < values.length ? "[" + values[index] + "]" : ""), "");}const price = 12;const qty = 3;The important rule is precise: for n placeholders, the tag receives n + 1 string pieces. There is a piece before the first placeholder, between each pair, and after the last one. Empty pieces still count.
function highlight(strings, ...values) { return strings.reduce((text, part, index) => text + part + (index < values.length ? "[" + values[index] + "]" : ""), "");}function currency(strings, ...values) { return strings.reduce((text, part, index) => { const value = values[index]; const money = typeof value === "number" ? "₹" + value.toFixed(2) : String(value ?? ""); return text + part + (index < values.length ? money : ""); }, "");}function safeHtml(strings, ...values) { return strings.reduce((text, part, index) => text + part + (index < values.length ? escapeHtmlValue(values[index]) : ""), "");}Pick a tag, read the code, then run it. The previews show what the tag receives before it joins anything.
The HTML-escaping example is deliberately small: it escapes only interpolated values, replacing &, <, >, double quotes, and apostrophes. The fixed string pieces are code you wrote. User input belongs in values. The deeper lesson XSS & safe DOM updates will cover the security model in more detail.
String.raw: keep every backslash as typed
STEP THROUGHTemplate strings are normally cooked: escape sequences like \n and \t become a newline and a tab. String.raw is a built-in tag that reads the raw template pieces instead. Think of it as a photocopy that preserves every backslash.
Compare a normal template with String.raw. The source looks similar, but the string values are different.
script
const raw = String.raw`C:\new\table`;console.log(normal);console.log(raw);console.log(String.raw`\d+\sitems`);Use String.raw when the source text itself contains many backslashes: Windows paths, regular-expression sources, and examples where you want learners to see the exact escape sequence. One caveat: a JavaScript template literal cannot end with a single unescaped backslash before the closing backtick, because that backslash would try to escape the backtick.
String.raw only affects how escape sequences in the literal pieces are read. Placeholder values are still evaluated and converted normally.
Where you’ll use this
PRACTICALTemplate literals show up anywhere a program has to explain itself to a person: UI labels, analytics logs, errors, receipts, generated file paths, and test data. Tags are less common, but powerful when a team wants one consistent rule for a whole family of strings.
const user = "Ada";const unread = 4;const label = `${user} has ${unread} unread messages`; const lines = ["first", "second"];const report = `Report:${lines.join("\n")}`; const folder = "invoices";const filename = "april.csv";const path = String.raw`C:\exports\${folder}\${filename}`; const unsafeName = "<Ada>";const html = safeHtml`<strong>${unsafeName}</strong>`;`Hello ${name}`"Hello ${name}"highlight`Total: ${price}`String.raw`C:\new\table``first line second line`'Total: ${price}'html`<p>${comment}</p>`String.raw`\d+\sitems`
Sort each card by the kind of string feature it uses.
Common mistakes
WATCH FOR- “Quotes interpolate too.” They do not. The string
"Hello ${name}"prints the dollar sign, braces, and name literally. - “The braces are optional.” The dollar sign and both braces make the placeholder.
$nameis just text. - “Objects print like the console preview.” String conversion is not inspection. Plain objects become
[object Object]unless customized. - “Tags receive one finished string.” Tags receive the literal pieces and values separately. That separation is their power.
- “String.raw makes everything raw.” It preserves raw literal pieces, but placeholder expressions still run normally.
const name = "Ada";console.log("Hello ${name}"); const item = { title: "Tea" };console.log(`Item: ${item}`); const folder = "new";console.log(`C:${folder} able`);Practice exercises
5 EXERCISESPredict the exact line printed by this receipt program.
const item = "Notebook";
const qty = 3;
const price = 4;
console.log(`${qty} × ${item}: ₹${qty * price}`);const item = "Notebook";
const qty = 3;
const price = 4;
console.log(`${qty} × ${item}: ₹${qty * price}`);The template fills three blanks: quantity, item, and qty * price. JavaScript multiplies before converting the result to text.
Change the greeting so it prints the person’s actual name.
const name = "Mina";
console.log("Hello ${name}");const name = "Mina";
console.log(`Hello ${name}`);Backticks turn the string into a template literal, so ${name} reads the variable and inserts Mina.
Write or read a tag that uppercases the values but keeps the string pieces unchanged.
function upperValues(strings, ...values) {
return strings.reduce((text, part, index) => text + part + (index < values.length ? String(values[index]).toUpperCase() : ""), "");
}
console.log(upperValues`Hello ${"ada"}, status: ${"ready"}`);function upperValues(strings, ...values) {
return strings.reduce((text, part, index) => text + part + (index < values.length ? String(values[index]).toUpperCase() : ""), "");
}
console.log(upperValues`Hello ${"ada"}, status: ${"ready"}`);The tag receives strings separately from values, so it can uppercase values without shouting the fixed words Hello and status.
Use a tag to protect a simple HTML string from a dangerous value.
function escapeValue(value) {
return String(value).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
}
function html(strings, ...values) {
return strings.reduce((text, part, index) => text + part + (index < values.length ? escapeValue(values[index]) : ""), "");
}
const comment = "<img src=x onerror='alert(1)'>";
console.log(html`<p>${comment}</p>`);function escapeValue(value) {
return String(value).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
}
function html(strings, ...values) {
return strings.reduce((text, part, index) => text + part + (index < values.length ? escapeValue(values[index]) : ""), "");
}
const comment = "<img src=x onerror='alert(1)'>";
console.log(html`<p>${comment}</p>`);The tag trusts the literal <p> and </p> you wrote, but escapes the user-controlled comment value before inserting it.
Print the file path without turning parts of it into a newline or tab.
console.log(String.raw`C:\new\table\report.txt`);console.log(String.raw`C:\new\table\report.txt`);String.raw keeps the path’s backslashes as characters, so the output stays on one line.
Check your understanding
7 QUESTIONSQuestion 1 of 7What does a backtick greeting print?
Read the code, then predictconst name = "Ada"; console.log(`Hello ${name}`);Choose an answer to see the explanation.
Question 2 of 7What does a quoted greeting print?
Read the code, then predictconsole.log("Hello ${name}");Choose an answer to see the explanation.
Question 3 of 7What does an interpolated plain object print?
Read the code, then predictconst item = { name: "Tea" }; console.log(`${item}`);Choose an answer to see the explanation.
Question 4 of 7How many string pieces does the tag receive?
Read the code, then predictfunction tag(strings, ...values) { console.log(strings.length); console.log(values.length); } tag`A ${1} B ${2} C`;Choose an answer to see the explanation.
Question 5 of 7What does this print?
Read the code, then predictconsole.log(String.raw`C:\new\table`);Choose an answer to see the explanation.
Question 6 of 7Which value should a safe HTML tag escape?
Choose an answer to see the explanation.
Question 7 of 7What is preserved by a multi-line template literal?
Choose an answer to see the explanation.
Key takeaways
- Backticks create template literals; quotes create normal strings.
- Each placeholder evaluates a real expression, then string conversion inserts the result.
- Real newlines and indentation inside template literals are part of the string.
- Tagged templates call a function with string pieces and values before joining.
String.rawkeeps escape backslashes in literal pieces.
A template literal is a string form whose blanks can run JavaScript before the final text is produced.
Up next: Unicode & string internals.