JavaScriptbeginner

Template Literals & String Methods — The Complete Guide

Master JavaScript template literals, string interpolation, tagged templates, and essential string methods. Covers regex basics, trimming, padding, and real-world patterns.

14 min read·Published Mar 7, 2026
template-literalsstringsregexjavascript

What Are Template Literals?

Template literals are string literals that allow embedded expressions, multiline strings, and special constructs called tagged templates. Introduced in ES6 (ES2015), they use backticks (`) instead of single or double quotes.

// Old way — quotes
const greeting = 'Hello, ' + name + '!';

// New way — template literal
const greeting = `Hello, ${name}!`;

This might look like a small change, but template literals unlock capabilities that plain strings simply cannot match. By the end of this guide you will know how to use them alongside JavaScript's full arsenal of string methods.

Template Literal Syntax

Basic Usage

Wrap your string in backticks instead of quotes.

const simple = `This is a template literal`;
const withQuotes = `She said "hello" and he said 'hi'`;

console.log(simple);     // This is a template literal
console.log(withQuotes); // She said "hello" and he said 'hi'

Notice that you can freely use both single and double quotes inside backticks without escaping.

Multiline Strings

Before template literals, multiline strings required concatenation or escape characters.

// Old way — concatenation
const old = 'Line one\n' +
            'Line two\n' +
            'Line three';

// Old way — escape
const escaped = 'Line one\n\
Line two\n\
Line three';

// Template literal — just press Enter
const modern = `Line one
Line two
Line three`;

console.log(modern);
// Line one
// Line two
// Line three

The whitespace is preserved exactly as you type it. This makes template literals perfect for generating HTML, SQL queries, or any structured text.

function createCard(title, body) {
  return `
    <div class="card">
      <h2 class="card-title">${title}</h2>
      <p class="card-body">${body}</p>
    </div>
  `;
}

console.log(createCard('Welcome', 'Thanks for visiting'));

Watch out: the leading whitespace in each line becomes part of the string. If indentation matters (like in a <pre> block), you may need to trim it.

String Interpolation

The ${} syntax lets you embed any JavaScript expression inside a template literal.

Variables and Expressions

const name = 'Alice';
const age = 30;

// Variable interpolation
console.log(`Name: ${name}`);         // Name: Alice

// Arithmetic
console.log(`Next year: ${age + 1}`); // Next year: 31

// Ternary
console.log(`Status: ${age >= 18 ? 'adult' : 'minor'}`); // Status: adult

// Function calls
console.log(`Upper: ${name.toUpperCase()}`); // Upper: ALICE

Complex Expressions

Anything that returns a value works inside ${}.

const items = ['apple', 'banana', 'cherry'];

// Array method
const list = `Fruits: ${items.join(', ')}`;
console.log(list); // Fruits: apple, banana, cherry

// Object property access
const user = { first: 'John', last: 'Doe' };
console.log(`Full name: ${user.first} ${user.last}`); // Full name: John Doe

// Nested template literals
const table = `
| Name    | Score |
|---------|-------|
${[
  { name: 'Alice', score: 95 },
  { name: 'Bob', score: 87 },
].map(r => `| ${r.name.padEnd(7)} | ${String(r.score).padEnd(5)} |`).join('\n')}
`;
console.log(table);

Expression Evaluation Order

Expressions are evaluated left to right, and their results are converted to strings using the default toString() method.

const obj = {
  toString() {
    return 'custom string';
  }
};

console.log(`Object: ${obj}`); // Object: custom string

// Arrays call join(',') internally
console.log(`Array: ${[1, 2, 3]}`); // Array: 1,2,3

// Objects without custom toString
console.log(`Plain: ${{ a: 1 }}`);  // Plain: [object Object]

Tagged Templates

Tagged templates are an advanced feature. A tag is a function that processes a template literal, giving you full control over how the string is assembled.

How Tags Work

A tag function receives the string parts as an array, followed by each interpolated value as separate arguments.

function highlight(strings, ...values) {
  console.log('Strings:', strings);
  console.log('Values:', values);

  return strings.reduce((result, str, i) => {
    return result + str + (values[i] !== undefined ? `<mark>${values[i]}</mark>` : '');
  }, '');
}

const name = 'Alice';
const role = 'admin';

const output = highlight`User ${name} has role ${role}`;
console.log(output);
// Strings: ['User ', ' has role ', '']
// Values:  ['Alice', 'admin']
// User <mark>Alice</mark> has role <mark>admin</mark>

The flow looks like this:

Template:   `User ${name} has role ${role}`

                 strings[0]    strings[1]      strings[2]
                 v             v                v
  Splits into:  "User "  |  " has role "  |  ""
                       ^                ^
                   values[0]        values[1]
                   "Alice"          "admin"

Practical Tagged Template: HTML Escaping

Prevent XSS attacks by escaping user input automatically.

function safeHTML(strings, ...values) {
  const escapeHTML = (str) =>
    String(str)
      .replace(/&/g, '&amp;')
      .replace(/</g, '&lt;')
      .replace(/>/g, '&gt;')
      .replace(/"/g, '&quot;')
      .replace(/'/g, '&#39;');

  return strings.reduce((result, str, i) => {
    const value = values[i] !== undefined ? escapeHTML(values[i]) : '';
    return result + str + value;
  }, '');
}

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>

Practical Tagged Template: SQL Query Builder

function sql(strings, ...values) {
  const params = [];
  const query = strings.reduce((result, str, i) => {
    if (i < values.length) {
      params.push(values[i]);
      return result + str + `$${params.length}`;
    }
    return result + str;
  }, '');

  return { query, params };
}

const name = 'Alice';
const age = 30;

const result = sql`SELECT * FROM users WHERE name = ${name} AND age > ${age}`;
console.log(result.query);
// SELECT * FROM users WHERE name = $1 AND age > $2
console.log(result.params);
// ['Alice', 30]

String.raw

JavaScript provides a built-in tag called String.raw that returns the raw string without processing escape sequences.

// Normal template literal processes \n as newline
console.log(`Line1\nLine2`);
// Line1
// Line2

// String.raw keeps the literal characters
console.log(String.raw`Line1\nLine2`);
// Line1\nLine2

// Useful for Windows paths
const path = String.raw`C:\Users\alice\Documents`;
console.log(path); // C:\Users\alice\Documents

// Useful for regex patterns
const pattern = String.raw`\d+\.\d+`;
console.log(pattern); // \d+\.\d+

Essential String Methods

JavaScript strings come with a rich set of built-in methods. Let's cover the ones you will use most often.

Searching Within Strings

const sentence = 'The quick brown fox jumps over the lazy dog';

// includes — returns boolean
sentence.includes('fox');       // true
sentence.includes('cat');       // false
sentence.includes('Fox');       // false (case-sensitive)

// startsWith / endsWith
sentence.startsWith('The');     // true
sentence.endsWith('dog');       // true
sentence.endsWith('Dog');       // false

// indexOf — returns position or -1
sentence.indexOf('fox');        // 16
sentence.indexOf('cat');        // -1

// lastIndexOf — searches from end
const text = 'abcabc';
text.lastIndexOf('abc');        // 3

Extracting Substrings

Three methods extract parts of a string. Here is a comparison:

Method          Arguments              Negative indices?
──────────────────────────────────────────────────────────
slice(start, end)      start and end positions     Yes
substring(start, end)  start and end positions     No (treats as 0)
substr(start, length)  start position + length     Yes (deprecated)
const str = 'JavaScript';
//           0123456789

// slice — preferred method
str.slice(0, 4);    // 'Java'
str.slice(4);       // 'Script'
str.slice(-6);      // 'Script'
str.slice(-6, -3);  // 'Scr'

// substring — similar but no negative indices
str.substring(0, 4);  // 'Java'
str.substring(4, 0);  // 'Java' (auto-swaps if start > end)

// substr — deprecated, avoid
str.substr(4, 6);     // 'Script' (start, length)

Recommendation: Use slice() everywhere. It handles negative indices, behaves predictably, and works on both strings and arrays.

split — Breaking Strings Apart

const csv = 'apple,banana,cherry,date';

// Split by comma
csv.split(',');     // ['apple', 'banana', 'cherry', 'date']

// Split by comma with limit
csv.split(',', 2); // ['apple', 'banana']

// Split every character
'hello'.split(''); // ['h', 'e', 'l', 'l', 'o']

// Split by whitespace
'one two  three'.split(/\s+/); // ['one', 'two', 'three']

// Split by multiple delimiters
'a-b_c.d'.split(/[-_.]/); // ['a', 'b', 'c', 'd']

replace and replaceAll

const text = 'foo bar foo baz foo';

// replace — first occurrence only
text.replace('foo', 'qux');
// 'qux bar foo baz foo'

// replace with regex + global flag — all occurrences
text.replace(/foo/g, 'qux');
// 'qux bar qux baz qux'

// replaceAll — all occurrences (ES2021)
text.replaceAll('foo', 'qux');
// 'qux bar qux baz qux'

// replace with callback function
'hello world'.replace(/\b\w/g, (char) => char.toUpperCase());
// 'Hello World'

Trimming and Padding

// Trimming whitespace
const messy = '   hello world   ';
messy.trim();       // 'hello world'
messy.trimStart();  // 'hello world   '
messy.trimEnd();    // '   hello world'

// Padding — pad to target length
'5'.padStart(3, '0');      // '005'
'42'.padStart(5, ' ');     // '   42'
'hi'.padEnd(10, '.');      // 'hi........'
'abc'.padEnd(6, '12');     // 'abc121'

// Practical: format a table
const data = [
  { name: 'Alice', score: 95 },
  { name: 'Bob', score: 8 },
  { name: 'Charlie', score: 100 },
];

data.forEach(({ name, score }) => {
  console.log(`${name.padEnd(10)} ${String(score).padStart(5)}`);
});
// Alice          95
// Bob             8
// Charlie       100

repeat

'ha'.repeat(3);      // 'hahaha'
'-'.repeat(40);      // '----------------------------------------'
'abc'.repeat(0);     // ''

// Build a simple progress bar
function progressBar(percent) {
  const filled = Math.round(percent / 5);
  const empty = 20 - filled;
  return `[${'#'.repeat(filled)}${'-'.repeat(empty)}] ${percent}%`;
}

console.log(progressBar(75));
// [###############-----] 75%

Case Conversion and Character Access

const str = 'Hello World';

str.toUpperCase();  // 'HELLO WORLD'
str.toLowerCase();  // 'hello world'

// Character access
str.charAt(0);      // 'H'
str[0];             // 'H' (bracket notation)
str.charCodeAt(0);  // 72 (Unicode code point)
str.codePointAt(0); // 72 (handles surrogate pairs)

// at() — supports negative indices (ES2022)
str.at(0);          // 'H'
str.at(-1);         // 'd'
str.at(-5);         // 'W'

Regular Expressions with Strings

Regular expressions are patterns used to match character combinations. They pair with string methods to create powerful text processing tools.

Creating a RegExp

// Literal syntax (preferred)
const pattern1 = /hello/;
const pattern2 = /hello/gi; // g = global, i = case-insensitive

// Constructor syntax (when pattern is dynamic)
const searchTerm = 'hello';
const pattern3 = new RegExp(searchTerm, 'gi');

Common RegExp Patterns

Pattern     Meaning                   Example Match
─────────────────────────────────────────────────────
\d          any digit                 "7"
\D          any non-digit             "a"
\w          word character [a-zA-Z0-9_]  "x"
\W          non-word character        "!"
\s          whitespace                " "
\S          non-whitespace            "a"
.           any character (except \n) "z"
^           start of string           ^Hello
$           end of string             world$
+           one or more               \d+
*           zero or more              \w*
?           zero or one               colou?r
{n}         exactly n                 \d{3}
{n,m}       between n and m           \d{2,4}
[abc]       character class           [aeiou]
[^abc]      negated class             [^0-9]
(abc)       capture group             (\d+)-(\d+)
(?:abc)     non-capture group         (?:Mr|Mrs)
a|b         alternation               cat|dog

test — Check for a Match

const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

emailPattern.test('[email protected]');  // true
emailPattern.test('not-an-email');      // false
emailPattern.test('[email protected]');            // true

const phonePattern = /^\d{3}-\d{3}-\d{4}$/;
phonePattern.test('555-123-4567');      // true
phonePattern.test('5551234567');        // false

match — Extract Matches

const text = 'Call 555-1234 or 555-5678 for info';

// Without global flag — returns first match with details
const single = text.match(/\d{3}-\d{4}/);
console.log(single[0]);     // '555-1234'
console.log(single.index);  // 5

// With global flag — returns all matches as array
const all = text.match(/\d{3}-\d{4}/g);
console.log(all); // ['555-1234', '555-5678']

// Named capture groups
const dateStr = '2026-03-07';
const dateMatch = dateStr.match(/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/);
console.log(dateMatch.groups);
// { year: '2026', month: '03', day: '07' }

matchAll — Iterate Over All Matches (ES2020)

const text = 'Price: $10.99, Discount: $2.50, Total: $8.49';
const pricePattern = /\$(\d+\.\d{2})/g;

for (const match of text.matchAll(pricePattern)) {
  console.log(`Found ${match[0]} at index ${match.index}, value: ${match[1]}`);
}
// Found $10.99 at index 7, value: 10.99
// Found $2.50 at index 25, value: 2.50
// Found $8.49 at index 42, value: 8.49

replace with RegExp

// Remove all non-alphanumeric characters
'Hello, World! 123'.replace(/[^a-zA-Z0-9]/g, '');
// 'HelloWorld123'

// Swap first and last name
'Doe, John'.replace(/(\w+), (\w+)/, '$2 $1');
// 'John Doe'

// Mask credit card number
'4111222233334444'.replace(/(\d{4})(\d{8})(\d{4})/, '$1********$3');
// '4111********4444'

// Convert camelCase to kebab-case
'backgroundColor'.replace(/([A-Z])/g, '-$1').toLowerCase();
// 'background-color'

// Replace with function
'2026-03-07'.replace(
  /(\d{4})-(\d{2})-(\d{2})/,
  (_, year, month, day) => `${day}/${month}/${year}`
);
// '07/03/2026'

Practical Patterns

Building a Simple Template Engine

function render(template, data) {
  return template.replace(/\{\{(\w+)\}\}/g, (_, key) => {
    return key in data ? data[key] : `{{${key}}}`;
  });
}

const template = `Hello {{name}}, welcome to {{site}}!
Your role is {{role}}.`;

const result = render(template, {
  name: 'Alice',
  site: 'DevsOfUs',
});

console.log(result);
// Hello Alice, welcome to DevsOfUs!
// Your role is {{role}}.

Slugify a String

function slugify(text) {
  return text
    .toLowerCase()
    .trim()
    .replace(/[^\w\s-]/g, '')   // remove non-word chars (except spaces and hyphens)
    .replace(/\s+/g, '-')       // replace spaces with hyphens
    .replace(/-+/g, '-');       // collapse multiple hyphens
}

slugify('Hello World! This is a Test');
// 'hello-world-this-is-a-test'

slugify('  Template Literals & String Methods  ');
// 'template-literals--string-methods'

Validate and Parse URLs

function parseURL(url) {
  const pattern = /^(https?):\/\/([^/:]+)(?::(\d+))?(\/[^?#]*)?(?:\?([^#]*))?(?:#(.*))?$/;
  const match = url.match(pattern);

  if (!match) return null;

  return {
    protocol: match[1],
    host: match[2],
    port: match[3] || null,
    path: match[4] || '/',
    query: match[5] || null,
    hash: match[6] || null,
  };
}

console.log(parseURL('https://example.com:8080/path?q=hello#section'));
// {
//   protocol: 'https',
//   host: 'example.com',
//   port: '8080',
//   path: '/path',
//   query: 'q=hello',
//   hash: 'section'
// }

Multiline String Processing

const logData = `
2026-03-07 10:00:00 INFO  Server started
2026-03-07 10:00:05 WARN  High memory usage
2026-03-07 10:00:10 ERROR Database connection failed
2026-03-07 10:00:15 INFO  Retry successful
`;

// Parse log entries
const entries = logData
  .trim()
  .split('\n')
  .map(line => {
    const match = line.match(/^(\S+ \S+)\s+(INFO|WARN|ERROR)\s+(.+)$/);
    if (!match) return null;
    return { timestamp: match[1], level: match[2], message: match[3] };
  })
  .filter(Boolean);

// Filter errors only
const errors = entries.filter(e => e.level === 'ERROR');
console.log(errors);
// [{ timestamp: '2026-03-07 10:00:10', level: 'ERROR', message: 'Database connection failed' }]

Method Chaining with Strings

Since most string methods return new strings, you can chain them for concise transformations.

const input = '  Hello, WORLD! This is A test.  ';

const result = input
  .trim()                              // 'Hello, WORLD! This is A test.'
  .toLowerCase()                       // 'hello, world! this is a test.'
  .replace(/[^a-z\s]/g, '')           // 'hello world this is a test'
  .split(/\s+/)                        // ['hello', 'world', 'this', 'is', 'a', 'test']
  .map(w => w[0].toUpperCase() + w.slice(1)) // ['Hello', 'World', 'This', 'Is', 'A', 'Test']
  .join(' ');                          // 'Hello World This Is A Test'

console.log(result); // Hello World This Is A Test

String Immutability

Strings in JavaScript are immutable. Every string method returns a new string; the original is never modified.

const original = 'hello';
const upper = original.toUpperCase();

console.log(original); // 'hello' (unchanged)
console.log(upper);    // 'HELLO' (new string)

// You cannot change individual characters
original[0] = 'H'; // silently fails (or throws in strict mode)
console.log(original); // 'hello'

This is why string operations can be expensive when done repeatedly in a loop. For heavy string building, consider using an array and joining at the end.

// Slow — creates many intermediate strings
let result = '';
for (let i = 0; i < 10000; i++) {
  result += `item ${i}, `;
}

// Fast — builds array, joins once
const parts = [];
for (let i = 0; i < 10000; i++) {
  parts.push(`item ${i}`);
}
const result = parts.join(', ');

Unicode and Special Characters

Template literals handle Unicode just like regular strings, but they pair well with modern Unicode-aware methods.

// Emoji and unicode in template literals
const emoji = `Hello ${'world'} \u{1F600}`;
console.log(emoji); // Hello world (grinning face emoji)

// String length vs actual characters
const face = '\u{1F600}';
console.log(face.length);         // 2 (surrogate pair)
console.log([...face].length);    // 1 (actual character count)

// Normalize unicode
const a1 = '\u00e9';      // e with acute (single code point)
const a2 = 'e\u0301';     // e + combining acute accent
console.log(a1 === a2);           // false
console.log(a1.normalize() === a2.normalize()); // true

Quick Reference Table

Method               Returns    Purpose
─────────────────────────────────────────────────────────────
str.includes(s)      boolean    contains substring?
str.startsWith(s)    boolean    starts with substring?
str.endsWith(s)      boolean    ends with substring?
str.indexOf(s)       number     position of first match (-1 if none)
str.slice(a, b)      string     extract from a to b
str.split(sep)       array      break into parts
str.replace(a, b)    string     replace first/all matches
str.replaceAll(a,b)  string     replace all matches
str.trim()           string     remove leading/trailing whitespace
str.padStart(n, c)   string     pad from start to length n
str.padEnd(n, c)     string     pad from end to length n
str.repeat(n)        string     repeat n times
str.toUpperCase()    string     all uppercase
str.toLowerCase()    string     all lowercase
str.at(i)            string     character at index (neg ok)
str.match(rx)        array      find regex matches
str.matchAll(rx)     iterator   iterate all regex matches
/rx/.test(str)       boolean    test if regex matches

Key Takeaways

  • Template literals use backticks and support multiline strings, interpolation with ${}, and tagged templates
  • Tagged templates give you full control over string assembly — great for HTML escaping, SQL parameterization, and DSLs
  • Use slice() for substring extraction — it is the most predictable and versatile option
  • split() and join() are inverse operations — use them together for text transformations
  • replace() with a regex and the g flag (or replaceAll()) handles global replacements
  • Regular expressions pair with test(), match(), matchAll(), and replace() for pattern-based text processing
  • Strings are immutable — every operation returns a new string
  • For heavy string building, use an array + join() instead of repeated concatenation
  • padStart() and padEnd() are perfect for formatting tabular output
  • Use String.raw when you need literal backslashes (file paths, regex patterns)

Found this helpful?

Support devsofus — help us keep creating free dev guides.

Related Articles