JavaScript Template Literals
Template literals are a way to build strings in JavaScript using backticks (`) instead of single or double quotes. They let you embed variables and expressions directly inside a string with ${...}, write multi-line text without special escape characters, and even intercept and transform a string’s construction using a tagged template function. Introduced in ES6 (2015), they replaced clunky string concatenation with something far more readable, and they are now the standard way modern JavaScript builds dynamic strings.
Overview: How Template Literals Work
A template literal is any string delimited by backtick characters instead of 'single' or "double" quotes: `like this`. On its own, a template literal behaves exactly like a normal string. What makes it powerful is interpolation: anywhere inside the backticks you can write ${expression}, and the JavaScript engine will evaluate that expression, convert the result to a string, and splice it into the final output.
Internally, the JavaScript engine treats a template literal as a sequence of alternating literal text and expression slots. When the engine reaches the template literal at runtime, it walks through the pieces in order: for each literal chunk it copies the text as-is, and for each ${...} slot it evaluates the expression in the current scope (so it has access to any variable, function call, or operator you would use anywhere else in your code), then converts the result to a string using the same internal ToString conversion that String(value) or implicit string coercion uses. Numbers become their decimal representation, booleans become "true"/"false", objects call their toString() method (or [Symbol.toPrimitive] if defined) unless overridden, and null/undefined become the literal strings "null"/"undefined". All the pieces are then concatenated into one final string value.
Because the expression inside ${} is evaluated fresh every time the template literal is executed, template literals are not cached or memoized—each evaluation reflects the current values of the variables involved, just like any other expression in your code.
Template literals also preserve literal newlines. Any actual line break you type between the backticks becomes a real \n character in the resulting string, which means you can write multi-line strings without concatenating with + or inserting explicit \n escapes. You can also nest template literals inside one another—a ${} slot can itself contain another backtick-delimited template literal, which is handy for conditional formatting.
Finally, a template literal can be tagged. If you place a function name (or any expression that evaluates to a function) immediately before the opening backtick, with no space, the engine does not build the string for you automatically. Instead it calls that function, passing it the literal text chunks as an array (with a .raw property containing the un-escaped, verbatim source text) followed by each interpolated value as a separate argument. The tag function decides how to combine them and returns whatever value it wants—a string, an object, anything. This is the mechanism behind libraries like styled-components (CSS-in-JS), GraphQL query tags, and safe HTML/SQL escaping helpers.
Syntax
const value = 42;
const text = `literal text ${value} more literal text`;
console.log(text);
| Part | Meaning |
|---|---|
` ` (backticks) |
Delimiters that mark the start and end of the template literal, replacing quotes. |
${expression} |
A placeholder holding any valid JavaScript expression (not a statement). It is evaluated and converted to a string, then inserted at that position. |
| Literal newlines | Pressing Enter inside the backticks inserts a real newline character into the string—no \n needed. |
tag`...` |
An optional function name placed directly before the opening backtick. Turns the literal into a tagged template call: tag(stringsArray, ...values). |
Examples
Example 1: Basic interpolation
const name = "Ava";
const age = 29;
const greeting = `Hello, ${name}! You are ${age} years old.`;
console.log(greeting);
const nextYear = `Next year you will be ${age + 1}.`;
console.log(nextYear);
Output:
Hello, Ava! You are 29 years old.
Next year you will be 30.
Both ${name} and ${age} are simple variable references, but ${age + 1} shows that a placeholder can hold any expression—arithmetic, function calls, ternaries, and more—not just a bare variable name.
Example 2: Multi-line strings with real data
const item = { name: "Wireless Mouse", price: 24.99, inStock: true };
const receipt = `
Order Summary
-------------
Item: ${item.name}
Price: $${item.price.toFixed(2)}
Status: ${item.inStock ? "In Stock" : "Out of Stock"}
`;
console.log(receipt);
Output:
Order Summary
-------------
Item: Wireless Mouse
Price: $24.99
Status: In Stock
Because the opening backtick is immediately followed by a line break, the resulting string begins with a newline (shown here as a blank first line), and each subsequent line becomes part of the string exactly as typed. Notice ${item.price.toFixed(2)} and the ternary ${item.inStock ? "In Stock" : "Out of Stock"}—method calls and conditional expressions work fine inside a placeholder, which is what makes template literals so much more expressive than plain concatenation.
Example 3: Tagged templates
function highlight(strings, ...values) {
return strings.reduce((result, str, i) => {
const value = values[i] !== undefined ? `[${values[i]}]` : "";
return result + str + value;
}, "");
}
const product = "Laptop";
const discount = 15;
const message = highlight`The ${product} is on sale with a ${discount}% discount!`;
console.log(message);
Output:
The [Laptop] is on sale with a [15]% discount!
Here highlight is called automatically with two arguments: an array of the literal text chunks (["The ", " is on sale with a ", "% discount!"]) and the interpolated values ("Laptop", 15) as the rest parameters. The tag function decides how to combine them—in this case wrapping each value in square brackets—instead of letting the engine build the string automatically. This is exactly the mechanism real-world libraries use to escape untrusted input, apply CSS-in-JS, or format localized strings.
Under the Hood: Step by Step
When the JavaScript engine encounters a template literal, it effectively does the following:
- The parser scans from the opening backtick to the matching closing backtick, splitting the source into an alternating list of literal text chunks and
${...}expression sources. - If the template literal is not tagged, the engine evaluates each expression left to right, in the current lexical scope, converts each result to a string via the ToString operation, and concatenates everything—literal chunks and converted values—into one final string.
- If the template literal is tagged, the engine instead builds an array of the literal chunks (frozen, with an extra
.rawarray holding the unprocessed source text, escape sequences included) and calls the tag function with that array as the first argument, followed by each expression’s evaluated value as additional arguments. Conceptually this is like the engine rewritingtag`a${x}b`intotag(["a", "b"], x). - For a given tagged-template call site, the engine reuses the same strings array object across repeated evaluations (for example inside a loop), which some libraries rely on as a cache key—but the interpolated values are always re-evaluated fresh each time.
- The final value, whether produced automatically or returned by a tag function, is then used wherever the template literal expression appears—assigned to a variable, passed to a function, logged, and so on.
Common Mistakes
Mistake 1: Using regular quotes and expecting interpolation
${} only triggers interpolation inside backticks. Inside single or double quotes it is just literal text.
const user = "Sam";
const wrong = "Hello, ${user}!";
console.log(wrong);
const right = `Hello, ${user}!`;
console.log(right);
Output:
Hello, ${user}!
Hello, Sam!
The first line prints the placeholder text verbatim because "..." is an ordinary string, not a template literal. Swapping the quote characters for backticks (right) is the only thing that activates interpolation.
Mistake 2: Putting a statement inside a placeholder
A ${} slot must contain a JavaScript expression, not a statement. Writing something like ${ if (score > 90) { "A" } } is invalid and throws a SyntaxError, because if blocks are statements, not values. To make a decision inside a placeholder, use an expression form instead—typically a ternary: ${score > 90 ? "A" : "B"}. If the logic is more involved than a simple ternary can express clearly, compute the value in a variable or a small function beforehand and reference that variable inside the placeholder instead of cramming logic into ${}.
Best Practices
- Prefer template literals over
+concatenation whenever a string mixes literal text with variables—it is easier to read and less error-prone. - Use template literals for multi-line strings instead of joining an array with
\nor chaining+ "\n" +. - Never assume template literals sanitize their input: interpolating untrusted data directly into an HTML string (e.g.
`<div>${userInput}</div>`) can introduce an XSS vulnerability. Escape or sanitize untrusted values before interpolating them into HTML, and prefer safe DOM APIs (liketextContent) when possible. - Keep expressions inside
${}short and readable. If a placeholder needs a multi-step calculation, compute it in a named variable first and reference the variable instead. - Reach for tagged templates when you need reusable, consistent processing of interpolated values—escaping, formatting numbers/currency, localization, or building CSS/SQL/GraphQL fragments safely.
- Watch indentation inside multi-line template literals: leading spaces you use to indent the code visually become part of the resulting string.
Practice Exercises
- Write a function
formatUser(user)that takes an object like{ firstName: "Lee", lastName: "Park", age: 34 }and returns a single template-literal string such as"Lee Park (34)". - Using a template literal, build a multi-line invoice string from an array of
{ name, qty, price }line items, with one line per item showing the item name, quantity, and line total (qty * price) formatted to two decimal places. - Write a tag function called
upperthat, when used asupper`hello ${name}`, returns the final string with every interpolated value converted to uppercase while leaving the literal text unchanged. Test it withname = "world"and confirm the output is"hello WORLD".
Summary
- Template literals use backticks (
` `) instead of quotes and support${expression}interpolation of any valid JavaScript expression. - Interpolated values are converted to strings via the same ToString conversion used elsewhere in JavaScript, then concatenated with the surrounding literal text.
- Literal newlines inside backticks become real
\ncharacters, making multi-line strings trivial to write. - Tagged templates (
tag`...`) call a function with the literal chunks and interpolated values as separate arguments, enabling custom string processing like escaping, formatting, or CSS-in-JS. - A
${}slot must contain an expression, never a statement; use ternaries or precomputed variables for conditional logic. - Template literals do not automatically sanitize interpolated values—escape untrusted data before inserting it into HTML or other sensitive contexts.
