Guide to JavaScript Template Literals & Strings

Master JavaScript template literals and string methods with practical examples. Learn string interpolation, tagged templates, common string operations, and modern patterns for building dynamic text.

JavaScriptbeginner
11 min read

Strings are one of the most frequently used data types in JavaScript. From displaying user names to building API URLs to formatting error messages, string operations are everywhere. Template literals, introduced in ES2015, transformed how developers work with strings by adding interpolation, multi-line support, and tagged template functionality. This guide covers everything from basic string creation to advanced template patterns you will use in production code.

Creating Strings: Three Syntaxes

JavaScript offers three ways to create strings. Each uses a different delimiter character.

javascriptjavascript
const single = 'Hello, World!';       // Single quotes
const double = "Hello, World!";       // Double quotes
const template = `Hello, World!`;     // Backticks (template literal)

Single and double quotes are functionally identical. They create the same string with the same methods and properties. The choice between them is purely a style convention (most projects pick one and stick with it).

Template literal (backticks) offer additional features that single and double quotes cannot provide.

FeatureSingle/Double QuotesTemplate Literals
Basic stringsYesYes
String interpolationNoYes (${expression})
Multi-line stringsNo (requires \n)Yes (literal newlines)
Embedded expressionsNoYes
Tagged templatesNoYes
Escape sequencesYesYes

String Interpolation with Template Literals

String interpolation lets you embed expressions directly inside a string using ${expression} syntax. This replaces the clunky string concatenation approach.

javascriptjavascript
const userName = "Ada";
const itemCount = 3;
const unitPrice = 29.99;
 
// Old approach: concatenation with +
const message1 = "Hello, " + userName + "! You have " + itemCount + " items ($" + (itemCount * unitPrice).toFixed(2) + ").";
 
// Modern approach: template literal interpolation
const message2 = `Hello, ${userName}! You have ${itemCount} items ($${(itemCount * unitPrice).toFixed(2)}).`;
 
console.log(message2);
// "Hello, Ada! You have 3 items ($89.97)."

The ${} syntax accepts any JavaScript expression, not just variables:

javascriptjavascript
const a = 10;
const b = 20;
 
console.log(`Sum: ${a + b}`);           // "Sum: 30"
console.log(`Max: ${Math.max(a, b)}`);  // "Max: 20"
console.log(`Even: ${a % 2 === 0}`);    // "Even: true"
console.log(`Type: ${typeof a}`);       // "Type: number"
 
// Function calls inside interpolation
const greet = (name) => `Hello, ${name}!`;
console.log(`${greet("Ada")}`); // "Hello, Ada!"
 
// Ternary expressions
const status = true;
console.log(`User is ${status ? "active" : "inactive"}`);
// "User is active"

Nesting Template Literals

You can nest template literals inside interpolation expressions for complex string building:

javascriptjavascript
const users = [
  { name: "Ada", role: "admin" },
  { name: "Grace", role: "editor" },
  { name: "Alan", role: "viewer" },
];
 
const userList = `
Users:
${users.map((u) => `  - ${u.name} (${u.role})`).join("\n")}
`;
 
console.log(userList);
// Users:
//   - Ada (admin)
//   - Grace (editor)
//   - Alan (viewer)

Multi-Line Strings

Template literals preserve line breaks exactly as written. With single or double quotes, you need escape sequences.

javascriptjavascript
// With single/double quotes (escape sequences required)
const html1 = "<div>\n  <h1>Title</h1>\n  <p>Content</p>\n</div>";
 
// With template literals (literal newlines preserved)
const html2 = `<div>
  <h1>Title</h1>
  <p>Content</p>
</div>`;
 
console.log(html1 === html2); // true

For a deeper exploration of multi-line string techniques, including how to handle indentation and whitespace, see the multi-line strings guide.

Whitespace Matters

Template literals preserve ALL whitespace, including indentation. If your template is indented inside a function, that indentation becomes part of the string. Use .trim() or a dedent utility to clean up unwanted leading whitespace.

Essential String Methods

JavaScript provides a rich set of methods on the String prototype. Here are the ones you will use most frequently:

Searching and Testing

javascriptjavascript
const email = "ada.lovelace@runehub.dev";
 
// includes() - check if substring exists
console.log(email.includes("@"));        // true
console.log(email.includes("yahoo"));    // false
 
// startsWith() / endsWith()
console.log(email.startsWith("ada"));    // true
console.log(email.endsWith(".dev"));     // true
 
// indexOf() - find position (returns -1 if not found)
console.log(email.indexOf("@"));        // 13
console.log(email.indexOf("xyz"));      // -1
 
// search() - find with regex
console.log(email.search(/\d/));        // -1 (no digits)
console.log(email.search(/@/));         // 13

Extracting and Slicing

javascriptjavascript
const filePath = "/users/ada/documents/report.pdf";
 
// slice(start, end) - extract substring (negative indices count from end)
console.log(filePath.slice(0, 6));      // "/users"
console.log(filePath.slice(-10));       // "report.pdf"
console.log(filePath.slice(1, -1));     // "users/ada/documents/report.pd"
 
// substring(start, end) - similar to slice but no negative indices
console.log(filePath.substring(7, 10)); // "ada"
 
// split() - divide string into array
const parts = filePath.split("/");
console.log(parts); // ["", "users", "ada", "documents", "report.pdf"]
 
// at() - access character by index (supports negative)
console.log(filePath.at(0));    // "/"
console.log(filePath.at(-1));   // "f"

Transforming

javascriptjavascript
const input = "  Hello, World!  ";
 
// Case transformation
console.log(input.toUpperCase());   // "  HELLO, WORLD!  "
console.log(input.toLowerCase());   // "  hello, world!  "
 
// Trimming whitespace
console.log(input.trim());         // "Hello, World!"
console.log(input.trimStart());    // "Hello, World!  "
console.log(input.trimEnd());      // "  Hello, World!"
 
// Replacing
const text = "foo-bar-baz";
console.log(text.replace("-", "_"));     // "foo_bar-baz" (first match only)
console.log(text.replaceAll("-", "_"));  // "foo_bar_baz" (all matches)
console.log(text.replace(/-/g, "_"));    // "foo_bar_baz" (regex global)
 
// Padding
const orderNum = "42";
console.log(orderNum.padStart(6, "0")); // "000042"
console.log(orderNum.padEnd(6, "."));   // "42...."
 
// Repeating
console.log("=".repeat(30)); // "=============================="

String Methods Reference Table

MethodPurposeReturns
includes(str)Check if substring existsBoolean
startsWith(str)Check if string starts with valueBoolean
endsWith(str)Check if string ends with valueBoolean
indexOf(str)Find first occurrence indexNumber (-1 if not found)
slice(start, end)Extract substringString
split(separator)Split into arrayArray
replace(old, new)Replace first matchString
replaceAll(old, new)Replace all matchesString
trim()Remove whitespace from both endsString
toLowerCase()Convert to lowercaseString
toUpperCase()Convert to uppercaseString
padStart(len, char)Pad from start to target lengthString
repeat(count)Repeat string n timesString
at(index)Get character at index (supports negative)String

Tagged Templates

Tagged templates are an advanced feature where a function processes a template literal. The function receives the string parts and interpolated values separately, letting you transform or validate the output.

javascriptjavascript
function highlight(strings, ...values) {
  return strings.reduce((result, str, i) => {
    const value = values[i] !== undefined ? `**${values[i]}**` : "";
    return result + str + value;
  }, "");
}
 
const name = "Ada";
const role = "engineer";
 
console.log(highlight`Welcome, ${name}! Your role is ${role}.`);
// "Welcome, **Ada**! Your role is **engineer**."

Practical: SQL Query Builder

Tagged templates shine for building safe queries that prevent injection:

javascriptjavascript
function sql(strings, ...values) {
  const query = strings.reduce((result, str, i) => {
    return result + str + (i < values.length ? `$${i + 1}` : "");
  }, "");
  
  return {
    text: query,
    values: values,
  };
}
 
const userId = 42;
const status = "active";
 
const query = sql`SELECT * FROM users WHERE id = ${userId} AND status = ${status}`;
 
console.log(query.text);   // "SELECT * FROM users WHERE id = $1 AND status = $2"
console.log(query.values); // [42, "active"]

Practical: HTML Escaping

javascriptjavascript
function safeHTML(strings, ...values) {
  const escape = (str) =>
    String(str)
      .replace(/&/g, "&amp;")
      .replace(/</g, "&lt;")
      .replace(/>/g, "&gt;")
      .replace(/"/g, "&quot;");
 
  return strings.reduce((result, str, i) => {
    return result + str + (i < values.length ? escape(values[i]) : "");
  }, "");
}
 
const userInput = '<script>alert("xss")</script>';
const safe = safeHTML`<div>${userInput}</div>`;
console.log(safe);
// "<div>&lt;script&gt;alert(&quot;xss&quot;)&lt;/script&gt;</div>"

Best Practices

Modern String Patterns

These practices reflect how strings are handled in modern JavaScript codebases.

Use template literals for any string that includes variables. The ${} syntax is more readable than concatenation and automatically handles type conversion. Reserve single/double quotes for plain strings with no dynamic content.

Always use replaceAll() or regex with /g for global replacements. The replace() method only replaces the first match by default. This is a common source of bugs when developers expect all occurrences to be replaced.

Use includes() instead of indexOf() for existence checks. str.includes("search") returns a boolean, which is more readable than str.indexOf("search") !== -1.

Prefer slice() over substring(). Both extract substrings, but slice() supports negative indices (counting from the end), making it more versatile. slice(-4) gets the last 4 characters.

Use trim() on all user input. Leading and trailing whitespace in form inputs causes matching failures and display issues. Always trim before processing or storing.

Common Mistakes and How to Avoid Them

String Pitfalls

These mistakes appear frequently in JavaScript code and cause subtle display or comparison bugs.

Forgetting that strings are immutable. Every string method returns a NEW string. The original is never modified. Writing name.toUpperCase() without assigning the result is a no-op.

javascriptjavascript
let name = "ada";
name.toUpperCase(); // Returns "ADA" but name is still "ada"
name = name.toUpperCase(); // Now name is "ADA"

Using replace() and expecting all matches to be replaced. This is one of the most common JavaScript bugs. Always use replaceAll() or the regex global flag.

Building strings with + in loops. Each concatenation creates a new string. For large loops, use an array and join() instead.

Comparing strings without normalizing case. "Admin" !== "admin". Always convert both sides to the same case before comparing: a.toLowerCase() === b.toLowerCase().

Confusing template literal backticks with single quotes. On many keyboards, backticks (`) are next to the 1 key. Using the wrong character produces a plain string without interpolation support.

Next Steps

Master multi-line strings

Learn advanced multi-line string techniques including indentation handling, HTML templates, and the dedent pattern.

Explore [JavaScript operators](/tutorials/programming-languages/javascript/js-operators-arithmetic-logical-comparison)

See how string operators interact with other data types and how template literals simplify type conversion in expressions.

Learn regular expressions

Pair your string knowledge with regex for powerful text pattern matching, validation, and transformation.

Build a template engine

Create a small template engine that uses tagged templates to render dynamic HTML with automatic escaping and conditional blocks.

Rune AI

Rune AI

Key Insights

  • Template literals use backticks: they support interpolation (${}), multi-line strings, and tagged templates
  • Interpolation accepts any expression: variables, function calls, ternary operators, and arithmetic all work inside ${}
  • Strings are immutable: every string method returns a new string and never modifies the original
  • Use replaceAll() for global replacements: replace() only handles the first match by default
  • Tagged templates enable safe string building: use them for SQL queries, HTML escaping, and internationalization
RunePowered by Rune AI

Frequently Asked Questions

What are template literals in JavaScript?

Template literals are strings delimited by backticks (`` ` ``) instead of single or double quotes. They support three features that regular strings do not: string interpolation with `${expression}`, multi-line strings with literal newlines, and tagged templates where a function processes the template. They were introduced in ES2015 (ES6).

What is the difference between template literals and regular strings?

Regular strings (single or double quotes) are static text. Template literals (backticks) can embed JavaScript expressions inside `${}`, span multiple lines without escape characters, and be processed by tag functions. Under the hood, they produce the same String type, so all string methods work on both.

When should I use template literals vs string concatenation?

Use template literals whenever a string includes dynamic values. The `${}` syntax is more readable than `"Hello, " + name + "!"` and handles type conversion automatically. Use regular quotes only for static strings with no variables, like `'[use strict](/tutorials/programming-languages/javascript/javascript-strict-mode-use-strict-explained)'` or simple object keys.

Can I nest template literals in JavaScript?

Yes. You can place template literals inside `${}` expressions of another template literal. This is useful for building complex strings with array methods like `map()` and `join()`. For example: `` `Items: ${items.map(i => `${i.name}: $${i.price}`).join(", ")}` ``.

What are tagged templates and when would I use them?

Tagged templates are functions that receive the string parts and interpolated values of a template literal separately, letting you transform the output. Common use cases include SQL query builders (preventing injection), HTML sanitizers (escaping user input), internationalization systems, and CSS-in-JS libraries like styled-components.

Conclusion

Template literals are the modern standard for string building in JavaScript, replacing concatenation with readable ${} interpolation, enabling multi-line strings without escape characters, and powering advanced patterns through tagged templates. Combined with JavaScript's string methods like includes(), slice(), replaceAll(), and trim(), you have a complete toolkit for every text operation. The most impactful habit is simple: use backticks for any string that includes variables and regular quotes for everything else.