Template literals & tagged templates
Build strings with expressions, preserve line breaks, process templates with tag functions, and keep backslashes with String.raw.
- 01Fill blanks in a stringUse
${}to put a variable, some math, a function call, or a ternary into a string. - 02Know the shape of the textTell when line breaks, spaces, and backslashes end up in the string.
- 03Write simple tagsWrite a function that gets the text pieces and the values separately, then changes the values.
Strings with blanks you can fill
A template literal is a string written between backticks, like `Hello`, instead of between quotes. On most keyboards, the backtick key is at the top left, under Esc.
Backticks give a string three extra powers:
- Blanks. A blank like
${name}is filled with a value when the code runs. - Real line breaks. The string can go over several lines.
- Tags. A function can look at the parts of the string before they are joined.
You met blanks in the Strings lesson. This lesson goes further. You will learn which text goes into each blank, where extra spaces come from, what a tag function gets, and why String.raw keeps backslashes.
Think of a printed form that says "Hello, ____." The printed words stay the same. The blank changes for each person. A template literal works the same way. Each blank holds a bit of JavaScript. JavaScript runs it and writes the answer into the blank.
- In real life: The printed words on the form
- In JavaScript: The fixed text between the backticks
- In real life: Each blank on the form
- In JavaScript: One ${...} with code inside
- In real life: The filled-in form
- In JavaScript: The finished string
A template literal is a string between backticks. Each blank, written ${...}, holds a piece of JavaScript. JavaScript runs that code, turns the answer into text, and puts the text where the blank was.
The longer examples are in code blocks. When you copy one, copy the backticks too. A backtick looks a bit like a quote, but it is a different character.
Filling the blanks
STEP THROUGHPutting a value into a piece of text is called interpolation. Each blank is called a placeholder. A placeholder starts with a dollar sign and an opening curly brace, ${, and ends with a closing curly brace, }.
Inside a placeholder you write an expression: any piece of code that gives back a value. It can be a variable, some math, or a function call. JavaScript fills the placeholders one by one, from left to right.
const user = "Asha";console.log(`Welcome, ${user}!`);Line 1 stores the text Asha in the variable user. Line 2 puts user between fixed words, so it prints Welcome, Asha!. The dollar sign and the curly braces only mark the blank. They do not show up in the result.
${user}. JavaScript reads the box user, puts its value into the blank, and gives back one finished string.const price = 3;const quantity = 2;console.log(`Tea costs ₹${price * quantity}`);Line 3 works out price * quantity first. That gives the number 6. Then JavaScript turns 6 into text and prints Tea costs ₹6. So a placeholder can hold any expression, not only a variable.
Choose the data, guess the final sentence, then step through to see how each ${} blank is filled.
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);Line 4 in the experiment does more than copy variables. It does three jobs:
- It counts the items with
items.length. - It picks
"item"or"items"with a ternary, a short if/else written ascondition ? a : b. - It joins the item names with
join, a method that puts all the list items into one string with a separator between them.
Each piece of code sits right where its answer goes in the sentence. That makes the code easy to read. It is why templates are a good fit for receipts, labels, log messages, and error messages.
const obj = { name: "Ada" };const custom = { toString() { return "custom object"; } };console.log(`${obj}`);console.log(`${custom}`);console.log(`${["a", "b", "c"]}`);A placeholder always turns its value into a string. It uses the same "string" conversion you met in the Object to primitive conversion lesson:
- Line 3: a plain object becomes
[object Object]. - Line 4: an object with its own
toStringmethod uses that method, so it printscustom object. - Line 5: an array becomes its items with commas between them,
a,b,c.
To get nicer text, call the method you want yourself, such as items.join(", ") or JSON.stringify(data). JSON.stringify turns an object into text that shows its properties.
A placeholder can hold another template literal. Keep this to one level. If a string starts to look like a puzzle, build the inner part first, store it in a variable, and put that variable in the blank.
Strings over several lines
STEP THROUGHA string in quotes must stay on one line. To get a line break inside it, you write \n, which means "new line". A template literal can hold a real line break. You just press Enter inside the backticks.
const message = `Hi, Asha!Have a good day.`;console.log(message);Line 1 starts the template and ends the first line after Hi, Asha!. Line 2 holds the second sentence and the closing backtick. The line break between them is part of the string, so line 3 prints two lines.
There is a catch. Every space between the backticks is part of the string too. That includes the spaces you add to line up your code.
Step through a string that goes over several lines, and see where the extra spaces in the output come from.
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 fix in the experiment is simple. It splits the string into lines, removes the spaces at both ends of each line with trim, and joins the lines again. This also removes spaces you wanted to keep. In bigger projects, people use a small helper function, often called dedent, that removes only the spaces that every line has in front.
The table compares the two kinds of strings. Read across a row to see how each one answers the question.
| Question | Quoted string | Template literal |
|---|---|---|
| What goes around the text? | Single quotes or double quotes. | Backticks. |
| Can it fill blanks? | No. ${name} prints as plain text. | Yes. ${name} is filled with the value of name. |
| How do you add a line break? | You write \n inside it. | You press Enter inside it, or write \n. |
| Are spaces kept? | Only the spaces you type inside the quotes. | Yes. Every space between the backticks is kept, even the indentation. |
Tagged templates: a function reads the form first
INTERACTIVEA tagged template is a template literal with a function name written right before it, like highlight`Total: ${price}`. That function is called the tag.
With a tag, JavaScript does not build the finished string. Instead, it calls the tag function and gives it two things:
- the fixed text pieces, in a list called
strings, - the values from the placeholders, one after another.
Whatever the tag function returns is the result.
Before the form is final, a clerk looks at it. The clerk can mark important values, write money amounts the right way, or make risky words harmless. Then the clerk hands back the finished form.
- In real life: The clerk sees the printed words
- In JavaScript: The strings list
- In real life: The clerk sees what was written in the blanks
- In JavaScript: The values
- In real life: The clerk stamps or rewrites the form
- In JavaScript: The tag returns whatever it wants
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`);Here is what each highlighted line does:
- Line 1 makes the tag function. Its first parameter,
strings, gets the fixed text pieces. Its second parameter,...values, is a rest parameter: it collects all the other arguments into one array. - Line 5 uses
reduce, an array method that goes through a list one item at a time and builds one result. Here it adds each text piece, then the next value inside square brackets. - Line 9 calls the tag by writing
highlightright before the template.
strings, and the filled-in values in values. Two blanks make three pieces.function countParts(strings, ...values) { console.log(strings.length); console.log(values.length);}countParts`Order ${3} for ${"tea"}`;This tag prints 3 and then 2. Two placeholders cut the text into three pieces. The last piece is the text after the last blank. Here it is empty, "", but it still counts. It is easy to forget, so remember it when you write a loop over the pieces.
A tag is a function call that gets the text pieces and the values. Step through to see what it gets.
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;Here is the rule. If a template has n placeholders, the tag gets n + 1 text pieces:
- one piece before the first placeholder,
- one piece between each pair of placeholders,
- one piece after the last placeholder.
An empty piece still counts.
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 its code, then run it. The panel shows what the tag gets before it joins anything.
The "Escape HTML values" tag in the lab makes risky text safe to show on a web page. It changes five characters in each value into safe codes: &, <, >, double quotes, and apostrophes. It changes only the values. The fixed pieces are code you wrote yourself, so you can trust them. Text typed by a user always arrives as a value. The later lesson XSS & safe DOM updates explains this kind of safety in full.
String.raw: keep every backslash as typed
STEP THROUGHA backslash, \, starts an escape sequence: a short code for a character that is hard to type. For example:
\nmeans a new line.\tmeans a tab, a wide space.
In a normal template, JavaScript swaps each escape sequence for the character it stands for. String.raw is a tag that comes with JavaScript. It skips that swap. It keeps each backslash and the letter after it, exactly as you typed them.
const digits = String.raw`\d+`;console.log(digits);Line 1 stores \d+: a backslash, the letter d, and a plus sign. Line 2 prints \d+. This text is a pattern for a regular expression, a tool for searching text. In the pattern, \d means "any digit", so the backslash must stay.
\n means "new line" and \t means "tab", so the path breaks apart. String.raw keeps each backslash as a plain character, so the path prints as typed.Compare a normal template with String.raw. The code looks almost the same, but the strings 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 text has many backslashes that you want to keep, for example:
- Windows file paths, like
C:\new\table, - regular expression patterns,
- lesson examples that should show an escape sequence as typed.
One warning: no template literal can end with a single backslash just before the closing backtick. JavaScript would read that backslash as an escape for the backtick, so the string would never end.
String.raw only changes how backslashes in the fixed text are read. Placeholders still run, and their values are still turned into text as usual.
const user = "Asha";console.log(String.raw`Hello\n${user}`);The \n stays as two characters: a backslash and the letter n. The ${user} placeholder still runs. So line 2 prints Hello\nAsha. Raw mode changes the fixed text, not what the placeholder does.
Where you'll use this
PRACTICALTemplate literals show up wherever a program writes a message for a person:
- labels on buttons and screens,
- log messages and error messages,
- receipts,
- file paths built from parts,
- sample text for tests.
Tags are less common. They help when a team wants one rule, like "always write money this way", for many strings.
const user = "Asha";const unread = 0;console.log(`${user} has ${unread} unread messages`);Line 3 prints Asha has 0 unread messages. The placeholder keeps the value you give it, even zero. Later code can pick "message" or "messages" if the page needs it.
const user = "Ada";const unread = 4;const label = `${user} has ${unread} unread messages`;console.log(label); const lines = ["first", "second"];const report = `Report:${lines.join("\n")}`;console.log(report); const folder = "invoices";const filename = "april.csv";const separator = "\\";const path = String.raw`C:\exports${separator}${folder}${separator}${filename}`;console.log(path); const unsafeName = "<Ada>";const html = safeHtml`<strong>${unsafeName}</strong>`;console.log(html);This code prints four results:
- Line 3 builds the label
Ada has 4 unread messages. - Lines 7 and 8 build a report. It keeps its two line breaks.
- Line 14 builds a Windows path with
String.raw. It stays on one line. - Line 18 uses the
safeHtmltag. The name<Ada>is made safe, so the result is<strong><Ada></strong>.
Each job is different, but each one keeps the changing value right next to the words around it.
Use a quoted string for text that never changes. Use a template literal when a few values change, or when you want real line breaks. Use a tag only when the same formatting or safety rule should apply to many strings. Most strings do not need a tag.
`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 it is. Look first at what goes around the text.
Common mistakes
WATCH FOR- "Quotes fill blanks too." They don't. The string
"Hello ${name}"prints the dollar sign, the braces, and the wordnameas plain text. - "The braces are optional." A placeholder needs the dollar sign and both curly braces.
$nameis just text. - "An object prints the way the console shows it." The console shows an object's properties. A placeholder only turns the object into a string, and a plain object becomes
[object Object]. - "A tag gets one finished string." A tag gets the fixed text pieces and the values separately. That is what lets it change the values.
- "String.raw stops everything from running." It only keeps the backslashes in the fixed text. Placeholders still run as usual.
const price = 3;console.log(`Price: $${price}`);Line 2 prints Price: $3. A dollar sign starts a placeholder only when a curly brace, {, comes right after it. The first dollar sign has no brace after it, so it prints as plain text.
const name = "Ada";console.log("Hello ${name}"); const item = { title: "Tea" };console.log(`Item: ${item}`); const drive = "C:";console.log(`${drive}\new\table`);Try to find each mistake before you read the list:
- Line 2 uses double quotes, so the blank is not filled.
- Line 5 puts an object in a blank, so it prints
[object Object]. - Line 8 is a normal template, not
String.raw, so\nbecomes a new line and\tbecomes a tab.
Practice exercises
5 EXERCISESWhat exact line does this receipt program print?
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 has three blanks: qty, item, and qty * price. JavaScript works out 3 times 4 first, then puts 12 into the text.
Change the greeting so it prints the person's real name. What does the fixed line print?
const name = "Mina";
console.log("Hello ${name}");const name = "Mina";
console.log(`Hello ${name}`);Backticks make the string a template literal. Now ${name} reads the variable and puts in Mina.
This tag puts each value in capital letters but leaves the fixed text alone. What does it print?
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 gets the text pieces and the values separately. So it can put ada and ready in capital letters without changing the fixed words Hello and status.
This tag makes a risky value safe before it goes into web page code. What exact text does it print?
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 <p> and </p> you wrote. It changes the comment, which came from a user: < becomes <, > becomes >, and each apostrophe becomes '.
This code prints a file path without turning parts of it into a new line or a tab. What does it print?
console.log(String.raw`C:\new\table\report.txt`);console.log(String.raw`C:\new\table\report.txt`);String.raw keeps each backslash as a plain character, so the path prints on one line.
Check your understanding
7 QUESTIONSQuestion 1 of 7What does this greeting in backticks print?
Read the code, then predictconst name = "Ada"; console.log(`Hello ${name}`);Choose an answer to see the explanation.
Question 2 of 7What does this greeting in double quotes print?
Read the code, then predictconsole.log("Hello ${name}");Choose an answer to see the explanation.
Question 3 of 7What does a plain object in a blank print?
Read the code, then predictconst item = { name: "Tea" }; console.log(`${item}`);Choose an answer to see the explanation.
Question 4 of 7How many text pieces and values does the tag get?
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 parts should a tag that makes web page text safe change?
Choose an answer to see the explanation.
Question 7 of 7What does a template literal over several lines keep?
Choose an answer to see the explanation.
Key takeaways
- Backticks make template literals. Quotes make normal strings.
- Each placeholder runs a piece of code, turns the answer into text, and puts it in the blank.
- Line breaks and spaces between the backticks are part of the string.
- A tag is a function. It gets the text pieces and the values separately, before anything is joined.
String.rawkeeps the backslashes in the fixed text exactly as typed.
A template literal is a string with blanks. JavaScript fills each blank by running the code inside it.
Up next: Unicode & string internals.