Template Literals in JavaScript: Write Strings the Modern Way
Every JavaScript developer has written something like this:
var greeting = "Hello, " + firstName + " " + lastName + "! You have " + count + " messages.";
It works. But it is painful to read, painful to write, and painful to debug when something goes wrong. Add a missing space, forget a closing quote, or nest a few more variables in, and you are staring at a wall of plus signs trying to find the bug.
Template literals, introduced in ES6, replace this entire pattern with something clean, readable, and far more powerful. This guide covers everything you need to know — from the basic syntax to real-world use cases — with a close look at how much simpler your strings become.
The Problem with Traditional String Concatenation
Before template literals, JavaScript had two ways to write strings: single quotes and double quotes. Both share the same fundamental limitation — they cannot contain dynamic values. To embed a variable, you break out of the string, use the + operator, and then start a new string literal.
This gets unwieldy fast.
// A simple greeting
var name = "Priya";
var age = 28;
var city = "Kolkata";
var bio = "My name is " + name + ", I am " + age + " years old, and I live in " + city + ".";
Count the quotes. Count the plus signs. Now imagine adding two more variables. The problems compound:
Readability collapses. The actual message you are trying to construct is buried inside a thicket of operators and delimiters. You cannot glance at the code and immediately understand what string it produces.
Spacing errors are invisible. A missing space before or after a variable — "Hello,"+ name vs "Hello, " + name — produces a bug that is easy to miss in a code review.
Multi-line strings require hacks. If you want a string that spans multiple lines, you either concatenate with \n escape sequences, or use backslash continuation — both of which are fragile and hard to read:
var html = "<div class=\"card\">\n" +
" <h2>" + title + "</h2>\n" +
" <p>" + description + "</p>\n" +
"</div>";
That backslash-n approach works, but it looks nothing like the HTML it produces. Anyone reading this code has to mentally reconstruct the output.
Quote conflicts are constant. If your string contains a single quote, you use double quotes as the delimiter, and vice versa. If it contains both, you reach for backslash escapes:
var message = "It's a \"great\" day";
var other = 'It\'s a "great" day';
Template literals solve all of these problems in one move.
Template Literal Syntax
A template literal uses backticks ` instead of single or double quotes. That is the only delimiter change — but it unlocks an entirely different set of capabilities.
const greeting = `Hello, world`;
On its own, this is identical to "Hello, world". The power comes from what you can put inside.
String interpolation with ${}
To embed any JavaScript expression inside a template literal, wrap it in ${}:
const name = "Arjun";
const greeting = `Hello, ${name}!`;
// "Hello, Arjun!"
The expression inside ${} is evaluated at runtime and its result is converted to a string and inserted. It is not limited to simple variables — any valid JavaScript expression works:
const a = 10;
const b = 3;
`\({a} + \){b} = ${a + b}` // "10 + 3 = 13"
`${a > b ? "greater" : "smaller"}` // "greater"
`${[1, 2, 3].join(", ")}` // "1, 2, 3"
`${"hello".toUpperCase()}` // "HELLO"
The ${} syntax turns your string into a live template. You write the message naturally, and the values drop in where they belong.
Before vs After: The Difference in Practice
Looking at the same string written both ways is the clearest way to understand what template literals change.
Simple variable embedding
const user = "Meera";
const score = 94;
// Before
const result = "Well done, " + user + "! Your score is " + score + " out of 100.";
// After
const result = `Well done, \({user}! Your score is \){score} out of 100.`;
The after version reads like natural language. The structure of the sentence is visible without having to mentally parse the concatenation.
Expressions inside strings
const price = 250;
const quantity = 3;
const tax = 0.18;
// Before
var total = "Total: Rs. " + (price * quantity * (1 + tax)).toFixed(2);
// After
const total = `Total: Rs. ${(price * quantity * (1 + tax)).toFixed(2)}`;
Inline expressions are cleaner in template literals because the expression is a natural part of the string — you do not need to construct the expression separately and concatenate it.
HTML strings
const product = { name: "Mechanical Keyboard", price: 3500, rating: 4.7 };
// Before
var card = "<div class=\"product\">" +
"<h3>" + product.name + "</h3>" +
"<p>Rs. " + product.price + "</p>" +
"<span>" + product.rating + " stars</span>" +
"</div>";
// After
const card = `
<div class="product">
<h3>${product.name}</h3>
<p>Rs. ${product.price}</p>
<span>${product.rating} stars</span>
</div>
`;
The after version looks like actual HTML. You can read it, modify it, and reason about it the same way you would a real template. Notice also that double quotes inside the string require no escaping — backticks do not conflict with them.
Multi-line Strings
This is where template literals save the most frustration. A backtick string can simply span multiple lines — the newlines are preserved in the output.
const poem = `Roses are red,
Violets are blue,
Template literals are clean,
And so is this haiku.`;
That is it. No \n. No string concatenation. No backslash continuation. The string you write is the string you get.
Compare this to the old approach, where you had to manually encode every line break:
// Before: you had to think about line breaks explicitly
var poem = "Roses are red,\n" +
"Violets are blue,\n" +
"Template literals are clean,\n" +
"And so is this haiku.";
The before version requires you to remember \n at the end of every line, keep the concatenation operators consistent, and mentally reconstruct what the output looks like. The after version is self-documenting.
Practical multi-line use: HTML templates
function renderUserCard(user) {
return `
<div class="user-card">
<img src="\({user.avatar}" alt="\){user.name}" />
<div class="user-info">
<h2>${user.name}</h2>
<p>${user.bio}</p>
<a href="mailto:\({user.email}">\){user.email}</a>
</div>
</div>
`;
}
You can read this function and immediately understand what HTML it produces. That was nearly impossible with the concatenation approach.
Practical multi-line use: SQL queries
const query = `
SELECT users.name, orders.total
FROM users
JOIN orders ON users.id = orders.user_id
WHERE orders.created_at > '${startDate}'
AND orders.status = 'completed'
ORDER BY orders.total DESC
LIMIT ${limit}
`;
SQL written this way is indented properly, readable at a glance, and easy to edit. With concatenation, SQL queries become nearly incomprehensible.
Nesting Template Literals
Template literals can be nested — you can have a template literal inside ${}:
const items = ["Mango", "Banana", "Guava"];
const list = `
<ul>
\({items.map(item => `<li>\){item}</li>`).join("\n ")}
</ul>
`;
Output:
<ul>
<li>Mango</li>
<li>Banana</li>
<li>Guava</li>
</ul>
This is a pattern you will use frequently when building HTML or JSX dynamically. The outer template provides the structure, and the inner template handles each item in the list.
Tagged Template Literals
Template literals have one more capability that goes beyond string construction: tagging. A tagged template is a function that receives the template's parts and interpolated values as separate arguments, letting you control how the string is assembled.
function highlight(strings, ...values) {
return strings.reduce((result, str, i) => {
return result + str + (values[i] !== undefined ? `<mark>${values[i]}</mark>` : "");
}, "");
}
const name = "Rahul";
const role = "engineer";
highlight`Meet \({name}, a software \){role}.`
// "Meet <mark>Rahul</mark>, a software <mark>engineer</mark>."
The tag function (highlight) receives:
strings: an array of the static string parts —["Meet ", ", a software ", "."]...values: the interpolated values —["Rahul", "engineer"]
You interleave them however you like. This is how libraries like styled-components and sql-template-strings work under the hood — they use tagged templates to process interpolated values with custom logic.
A practical tag: safe SQL
A common and important use of tagged templates is preventing SQL injection by escaping values:
function safeSQL(strings, ...values) {
const escaped = values.map(v => `'${String(v).replace(/'/g, "''")}'`);
return strings.reduce((q, str, i) => q + str + (escaped[i] ?? ""), "");
}
const userId = "1 OR 1=1"; // injection attempt
safeSQL`SELECT * FROM users WHERE id = ${userId}`;
// "SELECT * FROM users WHERE id = '1 OR 1=1'" — safely escaped
The value is treated as a literal string, not executable SQL, because the tag function controls how it is inserted.
Use Cases in Modern JavaScript
Template literals appear throughout modern JavaScript and its ecosystem. Here are the patterns you will encounter most often.
Dynamic URLs and API endpoints
const baseURL = "https://api.example.com";
const version = "v2";
const userId = 42;
const endpoint = `\({baseURL}/\){version}/users/${userId}/orders`;
// "https://api.example.com/v2/users/42/orders"
This is far easier to read than building URLs with concatenation, and far less error-prone — missing a slash between segments is immediately visible.
Error messages and logging
function validateAge(age) {
if (age < 0 || age > 120) {
throw new Error(`Invalid age: ${age}. Expected a value between 0 and 120.`);
}
}
Error messages written as template literals carry the actual bad value, making them useful for debugging without any extra logging effort.
Conditional strings
const count = 3;
const message = `You have \({count} new \){count === 1 ? "notification" : "notifications"}.`;
// "You have 3 new notifications."
Ternary expressions inside ${} handle pluralization and conditional text cleanly — no need to assemble the string in multiple steps.
CSS-in-JS
Libraries like styled-components use tagged template literals to write CSS directly in JavaScript:
import styled from "styled-components";
const Button = styled.button`
background: ${props => props.primary ? "#0070f3" : "white"};
color: ${props => props.primary ? "white" : "#0070f3"};
padding: 0.5rem 1.25rem;
border-radius: 4px;
`;
The CSS is readable, the dynamic values are natural, and the tag function handles the rest. This pattern would be completely impractical with concatenation.
Building email or notification templates
function buildEmailBody({ recipientName, senderName, orderId, total }) {
return `
Hi ${recipientName},
Your order #${orderId} has been confirmed.
Order summary:
- Total charged: Rs. ${total}
Thank you for shopping with us.
Best regards,
${senderName}
`.trim();
}
The template reads exactly like the email it will produce. Editing it is intuitive — you do not need to parse concatenation operators to understand the structure.
Things to Be Careful About
Whitespace is literal. Every space, tab, and newline in a template literal is part of the string. If you indent a template inside a function for readability, that indentation ends up in the output. Use .trim() to strip leading and trailing whitespace, or be deliberate about your indentation.
Backticks inside template literals need escaping. If your string itself needs to contain a backtick, escape it with a backslash:
const code = `Use backticks like \` this \` in template literals.`;
Do not put complex logic inside \({}. The \){} block is for expressions, not statements. Multi-step logic belongs in a variable outside the template:
// Harder to read — logic crammed into the template
const msg = `Status: ${(() => { if (a > b) return "high"; if (a < b) return "low"; return "equal"; })()}`;
// Better — compute it separately
const status = a > b ? "high" : a < b ? "low" : "equal";
const msg = `Status: ${status}`;
Template literals are not magic performance boosters. They are syntactic sugar — at runtime, they produce the same string. Choose them for readability, not performance.
Quick Reference
// Basic interpolation
`Hello, ${name}!`
// Expressions
`\({a + b}`, `\){condition ? "yes" : "no"}`, `${arr.join(", ")}`
// Multi-line
`Line one
Line two
Line three`
// Nested templates
`<ul>\({items.map(i => `<li>\){i}</li>`).join("")}</ul>`
// Tagged templates
tag`string with ${value} interpolated`
// Escaping backticks
`backtick: \``
// Stripping indentation whitespace
`
content here
`.trim()
Wrapping Up
Template literals are one of the highest-value additions in modern JavaScript. The improvement in readability alone justifies switching — but the additional capabilities, from multiline strings to tagged templates, make them genuinely more powerful than what they replace.
The migration is straightforward: anywhere you use string concatenation with variables, a template literal is almost certainly the better choice. It reads like the output it produces. The intent is clear. The bugs are easier to spot.
Start replacing + with ${} today, and you will quickly wonder how you tolerated the old way for so long.
Comments
Join the discussionNo comments yet. Be the first to comment.
