DEV Community

Harman Panwar
Harman Panwar

Posted on

Template Literals in JavaScript

Introduction

String manipulation is one of the most common tasks in JavaScript development. Whether you're building user interfaces, generating dynamic content, or constructing database queries, you'll find yourself working with strings constantly. For decades, JavaScript developers relied on string concatenation using the + operator to combine strings with variables. While this approach works, it often leads to code that is difficult to read, maintain, and debug.

ES6 introduced a powerful alternative: template literals. This feature transformed how developers work with strings in JavaScript, offering cleaner syntax, better readability, and powerful capabilities like multi-line strings and expression embedding. If you're still writing string concatenation the old way, you're missing out on a significantly better approach that has been available since 2015.

In this comprehensive guide, we'll explore everything you need to know about JavaScript template literals---from basic syntax to advanced use cases. We'll compare the old and new approaches side by side, examine real-world applications, and provide best practices that will help you write cleaner, more maintainable code.


The Problems with Traditional String Concatenation

Before we dive into template literals, it's essential to understand why the developer community embraced them so enthusiastically. Traditional string concatenation using the + operator works perfectly well for simple cases, but it quickly becomes problematic as your strings grow more complex.

Readability Issues

When you're concatenating multiple strings and variables, the code becomes a maze of quotes, plus signs, and whitespace. Consider this example of building an HTML string:

javascript

// Old way - concatenation
var html = '<div class="container">' +
           '<h1>' + title + '</h1>' +
           '<p>Welcome, ' + userName + '</p>' +
           '<p>You have ' + messageCount + ' new messages</p>' +
           '</div>';

Enter fullscreen mode Exit fullscreen mode

The nesting of quotes and operators makes it difficult to see the actual structure of the HTML being built. You have to mentally parse through the entire expression to understand what the output will look like. This becomes exponentially worse as the complexity increases.

Syntax Errors and Escaping

String concatenation requires careful management of quotes and escaping. When you need to include quotes within a string, you must escape them properly, which adds cognitive overhead:

javascript

// Old way - escaping quotes
var script = '<script>console.log("Hello, ' + userName + '");</script>';

Enter fullscreen mode Exit fullscreen mode

Each layer of nesting increases the risk of mismatched quotes or incorrect escaping. Debugging these issues can be frustrating, especially when the problem might be several lines away from where the error manifests.

Expression Clarity

When concatenating, mathematical expressions or function calls become buried in the concatenation syntax:

javascript

// Old way - complex expressions in concatenation
var message = 'Your total is $' + (price * quantity) + ' with ' +
              (discount > 0 ? (discount * 100) + '% off' : 'no discount');

Enter fullscreen mode Exit fullscreen mode

The mathematical logic is hidden within the string concatenation, making it harder to understand what calculations are being performed and how they affect the output.

Maintenance Challenges

As your codebase evolves, maintaining concatenated strings becomes increasingly difficult. Adding a new variable or changing the structure requires careful manipulation of multiple concatenation points:

javascript

// Old way - adding a new element requires modifying multiple locations
var greeting = 'Hello, ' + firstName + ' ' + lastName + '. ';
greeting += 'Today is ' + dayOfWeek + '. ';
greeting += 'Your appointment is at ' + time + '. ';

Enter fullscreen mode Exit fullscreen mode

Nested Quotes Problem

Perhaps the most frustrating aspect of traditional concatenation is dealing with nested quotes, especially when building HTML or SQL queries:

javascript

// Old way - double trouble with nested quotes
var html = '<input type="text" value="' + defaultValue + '" ' +
           'placeholder="' + placeholder + '" ' +
           'onclick="handleClick(\'' + inputId + '\')">';

Enter fullscreen mode Exit fullscreen mode

The mix of single and double quotes, combined with escaped characters, creates code that is nearly impossible to read and maintain.


Template Literal Syntax: The Basics

Template literals are enclosed by backtick characters (

```) instead of single or double quotes. They provide a clean, modern way to create strings with embedded expressions and multi-line support.

Simple Template Literal

Here's the most basic example of a template literal:

javascript


javascript
// Basic template literal
const greeting = `Hello, World!`;
console.log(greeting); // Output: Hello, World!



Enter fullscreen mode Exit fullscreen mode

This looks similar to regular strings, but the backticks unlock powerful features that quotes cannot provide.

Embedding Variables with Interpolation

The most transformative feature of template literals is expression interpolation---the ability to embed variables and expressions directly within a string using ${} syntax:

javascript


javascript
// Variable interpolation
const userName = 'Sarah';
const age = 28;

const bio = `My name is ${userName} and I am ${age} years old.`;
console.log(bio); // Output: My name is Sarah and I am 28 years old.



Enter fullscreen mode Exit fullscreen mode

The ${} syntax allows you to place any valid JavaScript expression inside the placeholder, and the result will be converted to a string and inserted into the final output.

Expression Evaluation

Not limited to simple variables---you can include complex expressions directly within the string:

javascript


javascript
// Embedded expressions
const price = 19.99;
const tax = 0.08;
const quantity = 3;

const total = `Total: $${(price * quantity * (1 + tax)).toFixed(2)}`;
console.log(total); // Output: Total: \$64.77



Enter fullscreen mode Exit fullscreen mode

The expression price * quantity * (1 + tax) is evaluated at runtime, and the result is formatted to two decimal places before being inserted into the string.


Multi-line Strings: A Game Changer

One of the most practical benefits of template literals is native support for multi-line strings. Previously, creating multi-line strings required workarounds like concatenation with newline characters or array joining.

Basic Multi-line Support

Template literals preserve whitespace and newlines exactly as written:

javascript


javascript
// Multi-line template literal
const poem = `Roses are red,
Violets are blue,
Template literals,
Made this possible for you.`;

console.log(poem);



Enter fullscreen mode Exit fullscreen mode

This outputs:


plaintext
Roses are red,
Violets are blue,
Template literals,
Made this possible for you.



Enter fullscreen mode Exit fullscreen mode

Building HTML Content

Multi-line template literals shine when building HTML structures:

javascript


javascript
// Building HTML with template literals
const title = 'Welcome';
const content = 'This is the content of our page.';

const html = `
  <article>
    <header>
      <h1>${title}</h1>
    </header>
    <main>
      <p>${content}</p>
    </main>
    <footer>
      <p>&copy; 2024 Your Company</p>
    </footer>
  </article>
`;

document.body.innerHTML = html;



Enter fullscreen mode Exit fullscreen mode

The HTML structure is now clear and readable, with variables cleanly embedded where they belong. You can see the final structure at a glance, making it much easier to spot errors and make modifications.

Indentation Considerations

While template literals preserve indentation, be aware that the indentation spaces are included in the output. For cleaner output, consider trimming or using template literal features strategically:

javascript


javascript
// Indentation is preserved
const template = `
  <div>
    <p>Line one</p>
    <p>Line two</p>
  </div>
`;

// The output will include the leading spaces from each line



Enter fullscreen mode Exit fullscreen mode

For building pre-formatted content like code blocks, this behavior is desirable. For generating clean HTML, you might want to consider trimming strategies.

Tagged Template Literals

For advanced string processing, template literals support tagged templates---functions that can parse template literals:

javascript


javascript
// Tagged template literal example
function highlight(strings, ...values) {
  let result = '';
  strings.forEach((string, i) => {
    result += string;
    if (i < values.length) {
      result += `<em>${values[i]}</em>`;
    }
  });
  return result;
}

const name = 'Alice';
const message = highlight`Hello, ${name}! Welcome aboard.`;

console.log(message);
// Output: Hello, <em>Alice</em>! Welcome aboard.



Enter fullscreen mode Exit fullscreen mode

Tagged templates receive the string parts and the interpolated values as separate arguments, allowing for sophisticated string transformation. This pattern is used by libraries like styled-components and GraphQL's query syntax.


Use Cases in Modern JavaScript

Template literals have become indispensable in modern JavaScript development. Let's explore some of the most common and powerful use cases.

Dynamic HTML Generation

Building HTML dynamically is one of the most frequent use cases for template literals:

javascript


javascript
// Generating a user card component
function createUserCard(user) {
  return `
    <div class="user-card">
      <img src="${user.avatar}" alt="${user.name}" class="user-avatar" />
      <div class="user-info">
        <h2 class="user-name">${user.name}</h2>
        <p class="user-role">${user.role}</p>
        <a href="mailto:${user.email}" class="user-email">${user.email}</a>
      </div>
    </div>
  `;
}

const cardHTML = createUserCard({
  name: 'Marcus Johnson',
  role: 'Software Engineer',
  email: 'marcus.j@techcorp.com',
  avatar: 'https://example.com/avatars/marcus.jpg'
});



Enter fullscreen mode Exit fullscreen mode

The structure is immediately clear, and it's trivial to add or modify fields without reconstructing concatenation chains.

SQL Query Building

Template literals make SQL query construction more readable:

javascript


javascript
// Building SQL queries
function buildUserQuery(filters) {
  const baseQuery = 'SELECT id, name, email FROM users';

  if (Object.keys(filters).length === 0) {
    return `${baseQuery} WHERE active = true`;
  }

  const conditions = Object.entries(filters)
    .map(([key, value]) => `${key} = '${value}'`)
    .join(' AND ');

  return `${baseQuery} WHERE ${conditions} AND active = true`;
}

const query = buildUserQuery({ department: 'Engineering', level: 'Senior' });
// Output: SELECT id, name, email FROM users WHERE department = 'Engineering' AND level = 'Senior' AND active = true



Enter fullscreen mode Exit fullscreen mode

While you should use parameterized queries in production to prevent SQL injection, template literals make query construction and debugging significantly easier during development.

Console Logging with Style

Template literals combined with CSS styling in console output:

javascript


javascript
// Styled console output
console.log(`
  %c Welcome to the Application
  ─────────────────────────────
  User: ${user.name}
  Role: ${user.role}
  Session: ${sessionId}
  ─────────────────────────────
`, 'color: #4CAF50; font-weight: bold; font-size: 14px;');



Enter fullscreen mode Exit fullscreen mode

This creates visually appealing console output that makes debugging and development more pleasant.

Internationalization and Localization

Template literals work well with i18n systems:

javascript


javascript
// i18n with template literals
const translations = {
  greeting: (name) => `Hello, ${name}!`,
  itemCount: (count) => count === 1 ? `${count} item` : `${count} items`,
  welcomeMessage: (name, count) => `Welcome back, ${name}! You have ${count} ${count === 1 ? 'notification' : 'notifications'}.`
};

console.log(translations.welcomeMessage('Emma', 5));
// Output: Welcome back, Emma! You have 5 notifications.



Enter fullscreen mode Exit fullscreen mode

String Formatting

Template literals provide clean ways to format data:

javascript


javascript
// Formatting currency
function formatCurrency(amount, currency = 'USD') {
  const symbols = { USD: '$', EUR: '€', GBP: '£', JPY: '¥' };
  return `${symbols[currency]}${amount.toLocaleString('en-US', { minimumFractionDigits: 2 })}`;
}

console.log(formatCurrency(1234567.89, 'USD')); // \$1,234,567.89
console.log(formatCurrency(9876543.21, 'EUR')); // €9,876,543.21

// Formatting dates
function formatDate(date) {
  return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`;
}



Enter fullscreen mode Exit fullscreen mode

Dynamic Class Names

Constructing class names conditionally:

javascript


javascript
// Conditional class names
function getButtonClass(variant = 'primary', size = 'medium', disabled = false) {
  return `btn btn-${variant} btn-${size}${disabled ? ' btn-disabled' : ''}`;
}

console.log(getButtonClass());        // btn btn-primary btn-medium
console.log(getButtonClass('danger')); // btn btn-danger btn-medium
console.log(getButtonClass('ghost', 'large', true)); // btn btn-ghost btn-large btn-disabled



Enter fullscreen mode Exit fullscreen mode

URL and Path Construction

Building URLs dynamically:

javascript


javascript
// URL construction
function buildApiUrl(baseUrl, endpoint, params = {}) {
  const queryString = Object.entries(params)
    .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
    .join('&');

  return `${baseUrl}/${endpoint}${queryString ? '?' + queryString : ''}`;
}

const apiUrl = buildApiUrl('https://api.example.com', 'users', {
  page: 1,
  limit: 10,
  sort: 'created_at'
});

console.log(apiUrl);
// Output: https://api.example.com/users?page=1&limit=10&sort=created_at



Enter fullscreen mode Exit fullscreen mode

Side-by-Side Comparison: Old vs New

Let's directly compare the traditional concatenation approach with template literals across several common scenarios.

Simple Variable Insertion

javascript


javascript
// Old Way
var name = 'John';
var greeting = 'Hello, ' + name + '!';
console.log(greeting); // Hello, John!

// New Way
const name = 'John';
const greeting = `Hello, ${name}!`;
console.log(greeting); // Hello, John!



Enter fullscreen mode Exit fullscreen mode

Winner: Template Literals --- The variable is clearly marked and the string structure is immediately apparent.

Complex Expressions

javascript


javascript
// Old Way
var a = 5, b = 10;
var result = 'The sum of ' + a + ' and ' + b + ' is ' + (a + b) + '.';
console.log(result); // The sum of 5 and 10 is 15.

// New Way
const a = 5, b = 10;
const result = `The sum of ${a} and ${b} is ${a + b}.`;
console.log(result); // The sum of 5 and 10 is 15.



Enter fullscreen mode Exit fullscreen mode

Winner: Template Literals --- The mathematical expression is visually separated and clearer.

Multi-line Content

javascript


javascript
// Old Way
var html = '<div class="card">' +
          '  <h2>Title</h2>' +
          '  <p>Content goes here</p>' +
          '</div>';

// New Way
const html = `
  <div class="card">
    <h2>Title</h2>
    <p>Content goes here</p>
  </div>
`;



Enter fullscreen mode Exit fullscreen mode

Winner: Template Literals --- The HTML structure is readable and maintainable.

HTML with Attributes

javascript


javascript
// Old Way
var img = '<img src="' + imageUrl + '" alt="' + altText + '" class="' + className + '" />';

// New Way
const img = `<img src="${imageUrl}" alt="${altText}" class="${className}" />`;



Enter fullscreen mode Exit fullscreen mode

Winner: Template Literals --- Much cleaner attribute handling without quote conflicts.

Nested Conditionals

javascript


javascript
// Old Way
var status = isLoggedIn ? (isPremium ? 'Premium Member' : 'Standard Member') : 'Guest';

// New Way
const status = isLoggedIn
  ? (isPremium ? 'Premium Member' : 'Standard Member')
  : 'Guest';



Enter fullscreen mode Exit fullscreen mode

Winner: Template Literals --- The ternary expressions are clearer when embedded in template strings.

Object Property Access

javascript


javascript
// Old Way
var message = 'User ' + user.firstName + ' ' + user.lastName + ' has ' + user.items.length + ' items.';

// New Way
const { firstName, lastName, items } = user;
const message = `User ${firstName} ${lastName} has ${items.length} items.`;



Enter fullscreen mode Exit fullscreen mode

Winner: Template Literals + Destructuring --- The combination provides excellent readability.

Function Results

javascript


javascript
// Old Way
var output = 'The result is ' + calculateValue(x, y) + ' at ' + new Date().toISOString() + '.';

// New Way
const output = `The result is ${calculateValue(x, y)} at ${new Date().toISOString()}.`;



Enter fullscreen mode Exit fullscreen mode

Winner: Template Literals --- Function calls are clearly embedded.

SQL Queries

javascript


javascript
// Old Way
var query = "SELECT * FROM users WHERE name = '" + userName + "' AND status = '" + status + "'";

// New Way
const query = `SELECT * FROM users WHERE name = '${userName}' AND status = '${status}'`;



Enter fullscreen mode Exit fullscreen mode

Winner: Template Literals --- The SQL structure is much clearer (though parameterized queries are still recommended for security).


Advanced Features and Techniques

Nested Template Literals

Template literals can be nested within each other for complex scenarios:

javascript


javascript
// Nested template literals
const countries = ['USA', 'UK', 'Germany'];
const cities = ['New York', 'London', 'Berlin'];

const locations = countries.map((country, i) =>
  `${country}: ${cities[i]}`
).join('\n');

console.log(locations);
// USA: New York
// UK: London
// Germany: Berlin



Enter fullscreen mode Exit fullscreen mode

Template Literals with Functions

Pass template literals to functions for templating systems:

javascript


javascript
// Simple templating system
function formatTemplate(template, data) {
  return template.replace(/\${(\w+)}/g, (match, key) => data[key] ?? '');
}

const template = 'Hello, ${name}! Your order ${orderId} is ready.';
const data = { name: 'Alice', orderId: 'ORD-12345' };

console.log(formatTemplate(template, data));
// Hello, Alice! Your order ORD-12345 is ready.



Enter fullscreen mode Exit fullscreen mode

Using Methods Inside Expressions

You can call string methods directly within template literals:

javascript


javascript
// Method calls in template literals
const words = 'hello world';
const result = `Original: "${words}" → Upper: "${words.toUpperCase()}"`;
console.log(result); // Original: "hello world" → Upper: "HELLO WORLD"



Enter fullscreen mode Exit fullscreen mode

Ternary Operators

Embed conditional logic directly in your strings:

javascript


javascript
// Ternary in template literals
const isActive = true;
const status = `User is ${isActive ? 'active' : 'inactive'}`;
console.log(status); // User is active



Enter fullscreen mode Exit fullscreen mode

Arithmetic Operations

Perform calculations within template literals:

javascript


javascript
// Calculations in template literals
const width = 100;
const height = 200;
const aspectRatio = `Aspect ratio: ${width / height}`; // 0.5
const area = `Area: ${width * height}`; // 20000



Enter fullscreen mode Exit fullscreen mode

Best Practices

1. Choose Descriptive Variable Names

javascript


javascript
// Poor
const m = `Hello, ${n}`;

// Better
const greeting = `Hello, ${name}`;



Enter fullscreen mode Exit fullscreen mode

2. Handle Undefined and Null Values

Be careful with potentially undefined values:

javascript


javascript
// Handle undefined gracefully
const userName = user?.name ?? 'Guest';
const message = `Welcome, ${userName}!`;



Enter fullscreen mode Exit fullscreen mode

3. Escape Dollar Signs When Needed

Use backslash to escape dollar signs that shouldn't be interpreted as expressions:

javascript


javascript
// Escaping $ in template literals
const price = 19.99;
const display = `The price is \${${price}}`; // The price is ${19.99}



Enter fullscreen mode Exit fullscreen mode

4. Use Template Literals Consistently

Pick a style and stick with it throughout your codebase. Mixing string concatenation with template literals creates inconsistency:

javascript


javascript
// Consistent approach
const first = `Hello`;
const second = `World`;
const combined = `${first} ${second}`; // Not: first + ' ' + second



Enter fullscreen mode Exit fullscreen mode

5. Consider Readability for Complex Expressions

For very complex expressions, consider breaking them into variables first:

javascript


javascript
// For readability - extract complex expressions
const totalPrice = (price * quantity * (1 - discount)).toFixed(2);
const summary = `Total: $${totalPrice}`;



Enter fullscreen mode Exit fullscreen mode

6. Use Template Literals for HTML Construction

Template literals are ideal for building HTML strings, but consider using DOM APIs or template libraries for complex scenarios to prevent XSS vulnerabilities.

7. Leverage Tagged Templates for Validation

Tagged templates can be used for input sanitization and validation:

javascript


javascript
// Simple HTML escaping tagged template
function escape(strings, ...values) {
  const escaped = values.map(v => String(v)
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')
    .replace(/"/g, '&quot;')
    .replace(/'/g, '&#39;')
  );

  return strings.reduce((acc, str, i) => acc + str + (escaped[i] || ''), '');
}

const userInput = '<script>alert("xss")</script>';
const safe = escape`User said: ${userInput}`;



Enter fullscreen mode Exit fullscreen mode

Browser Support and Transpilation

Template literals have excellent browser support:

  • Chrome: Supported (version 41+, since 2015)
  • Firefox: Supported (version 34+, since 2014)
  • Safari: Supported (version 9+, since 2015)
  • Edge: Supported (version 12+, since 2015)
  • Node.js: Supported (version 4+, since 2015)

For older browser support, transpilers like Babel can convert template literals to ES5-compatible code. Most modern projects using build tools like webpack, vite, or parcel automatically handle this transformation.


Common Pitfalls to Avoid

1. Accidentally Using Single Quotes

javascript


javascript
// Wrong - single quotes won't enable interpolation
const greeting = 'Hello, ${name}!'; // Output: Hello, ${name}!

// Correct - use backticks
const greeting = `Hello, ${name}!`;



Enter fullscreen mode Exit fullscreen mode

2. Forgetting to Close the Template

Ensure every opening backtick has a matching closing backtick:

javascript


javascript
// Wrong - missing closing backtick
const broken = `Incomplete string;

// Correct
const complete = `Complete string`;

```

### 3\. Overlooking White-space in Output

Remember that newlines and indentation in template literals are preserved:

javascript

```javascript
// If you don't want the leading newline
const clean = `Line 1
Line 2
Line 3`;

// vs stripping first line
const stripped = `Line 1
Line 2`.trimStart();

```

### 4\. Not Handling Null or Undefined

Interpolation of `null` or `undefined` produces the string "null" or "undefined":

javascript

```javascript
// This will produce "Hello, null!"
const name = null;
const greeting = `Hello, ${name}!`; // Hello, null!

// Handle with nullish coalescing
const safe = `Hello, ${name ?? 'Guest'}!`; // Hello, Guest!

```

* * * * *

Conclusion
----------

Template literals represent one of the most significant improvements to JavaScript's string handling capabilities. They solve the readability and maintainability problems that plagued traditional concatenation, provide native multi-line support, and enable powerful patterns through tagged templates.

The evidence is clear: template literals produce code that is easier to read, write, and maintain. The interpolation syntax makes variables and expressions stand out, the multi-line support eliminates complex concatenation chains, and the backtick syntax avoids the quote-escaping nightmares that plagued older code.

If you're still writing code like `'Hello, ' + name + '!'`, it's time to modernize your approach. The `${}` syntax and backtick notation should become second nature to every JavaScript developer.

Key Takeaways:

1.Template literals use backticks (```) instead of quotes

2.Variables and expressions embed using `${expression}` syntax

3.Multi-line strings are fully supported with preserved whitespace

4.Tagged templates enable powerful string transformation

5.Always use template literals for complex string construction

6.Handle null and undefined values appropriately

7.Use destructuring for cleaner embedded property access

Start incorporating template literals into your daily JavaScript development workflow, and you'll quickly wonder how you ever managed without them. The improvement in code quality and developer experience is substantial and immediate.
Enter fullscreen mode Exit fullscreen mode

Top comments (0)