Back to blog

Sunday, March 2, 2025

Tagged Template Literals in JavaScript – How to use them for advanced string manipulation

cover

In the world of JavaScript, string manipulation is a common task. Whether you're formatting user messages, generating HTML, or creating complex queries, strings are at the heart of it all. While traditional string concatenation can get you basic results, it lacks flexibility and can often lead to messy code, especially when dealing with dynamic content.

Enter tagged template literals. Introduced in ES6, tagged template literals provide a powerful way to manipulate strings. They not only make your code more readable and maintainable but also enable you to create complex string transformations easily.

What are Tagged Template Literals?

At their core, template literals (using backticks) offer a cleaner way to embed expressions within strings. However, when you add a tag to these template literals, they transform into tagged template literals.

Here’s a basic example to illustrate the concept:

// A simple tag function
function myTag(strings, ...values) {
    console.log(strings);
    console.log(values);
}

// Using the tag function with a template literal
myTag`Hello, ${name}! Today is ${day}.`;

In this example, myTag is a tag function. When you call myTag with a template literal, the function receives two parameters:

  • strings: An array of strings from the template literal, including the parts around the expressions. For example, in myTag example, strings would be ["Hello, ", "! Today is ", "."].
  • ...values: An array of values from the expressions inside the template literal. For example, values would be [name, day].

Basic Usage of Tagged Template Literals

Let’s start with a more practical example. Suppose we want to create a function that safely escapes HTML to prevent XSS (Cross-Site Scripting) attacks.

function htmlEscape(strings, ...values) {
    // Join the strings and values, escaping the values
    let result = '';
    for (let i = 0; i < strings.length; i++) {
        result += strings[i];
        if (i < values.length) {
            // Escape special HTML characters in values
            result += String(values[i])
                .replace(/&/g, "&amp;")
                .replace(/</g, "&lt;")
                .replace(/>/g, "&gt;")
                .replace(/"/g, "&quot;")
                .replace(/'/g, "&##039;");
        }
    }
    return result;
}

const user = { name: "Alice <script>alert('XSS')</script>" };
const message = htmlEscape`Hello, ${user.name}!`;
console.log(message); // Outputs: "Hello, Alice &lt;script&gt;alert('XSS')&lt;/script&gt;!"

In this example, the htmlEscape function ensures that any HTML special characters in the name variable are properly escaped.

Advanced Use Cases of Tagged Template Literals

Creating a Stylish Logger

One of the most common uses of tagged template literals is to create logging functions that format strings in a more readable way. Here’s an example of a styled logger that outputs messages in different colors based on their severity.

function styledLogger(strings, ...values) {
    let message = '';
    for (let i = 0; i < strings.length; i++) {
        message += strings[i];
        if (i < values.length) {
            message += values[i];
        }
    }

    const styles = {
        debug: 'color: blue;',
        info: 'color: green;',
        warn: 'color: orange;',
        error: 'color: red;'
    };

    // Extract the log level from the first word
    const logLevel = message.split(' ')[0].toLowerCase();
    const style = styles[logLevel] || '';

    if (logLevel in styles) {
        console.log(`%c${message}`, style);
    } else {
        console.log(message);
    }
}

styledLogger`info This is an informational message.`;
styledLogger`error This is an error message.`;

In this example, the styledLogger function uses tagged template literals to log messages with different styles based on their severity level.

Localizing Strings

Tagged template literals can also be used for localization (l10n) purposes, where different languages are dynamically inserted into strings.

const locales = {
    'en': { greeting: 'Hello', farewell: 'Goodbye' },
    'es': { greeting: 'Hola', farewell: 'Adiós' },
    'fr': { greeting: 'Bonjour', farewell: 'Au revoir' }
};

function localize(strings, ...values) {
    const language = 'es'; // Current language setting
    const localStrings = locales[language];

    let result = '';
    for (let i = 0; i < strings.length; i++) {
        result += strings[i];
        if (i < values.length) {
            const valueKey = values[i];
            if (localStrings[valueKey]) {
                result += localStrings[valueKey];
            } else {
                result += valueKey;
            }
        }
    }
    return result;
}

const greetingMessage = localize`greeting, ${'name'}! farewell}`;
console.log(greetingMessage); // Outputs: "Hola, name! Adiós"

In this example, the localize function uses tagged template literals to replace placeholders with localized strings based on the current language setting.

Creating Complex Queries

Tagged template literals can be incredibly useful for creating complex queries, especially in database operations. Here’s an example using a SQL query:

function sql(strings, ...values) {
    // Construct a safe SQL query
    let result = '';
    for (let i = 0; i < strings.length; i++) {
        result += strings[i];
        if (i < values.length) {
            // Assuming values need to be properly escaped or parameterized
            result += `'${values[i]}'`; // Simplified example without actual SQL escaping
        }
    }
    return result;
}

const tableName = "users";
const userId = 123;
const query = sql`SELECT * FROM ${tableName} WHERE id = ${userId}`;
console.log(query); // Outputs: "SELECT * FROM users WHERE id = '123'"

Important Note: In real-world applications, you should use proper SQL escaping mechanisms to prevent SQL injection attacks.

Tagged Template Literals vs Regular Template Literals

To fully appreciate tagged template literals, it’s useful to compare them with regular template literals.

FeatureTagged Template LiteralsRegular Template Literals
SyntaxmyTag`Some ${expression} template ``Some ${expression} template `
Function CallCalls the myTag function with parts of the templateJust returns a single string
Use CasesAdvanced string manipulations, localization, formattingBasic interpolation, no additional processing
Parametersstrings, ...valuesexpression values
OutputAny type (string, object, array, etc.)Always a string

Creating a Custom Tag Function

Creating a custom tag function can greatly enhance your ability to manipulate strings dynamically. Here’s how you can create one:

  1. Define the Tag Function: This function will process the strings and values arrays.
  2. Process the Strings and Values: Manipulate the string parts and embedded expressions as needed.
  3. Return the Result: The result can be a string, object, or any other data type.

Here’s a step-by-step example of creating a custom tag function called highlightText that wraps certain words in HTML <strong> tags.

function highlightText(strings, ...values) {
    const keywords = ['highlight', 'important'];
    let result = '';

    for (let i = 0; i < strings.length; i++) {
        result += strings[i];
        if (i < values.length) {
            const value = values[i];
            if (keywords.includes(value)) {
                result += `<strong>${value}</strong>`;
            } else {
                result += value;
            }
        }
    }
    return result;
}

const message = highlightText`This is an ${'important'} message. Please read it carefully.`;
console.log(message); // Outputs: "This is an <strong>important</strong> message. Please read it carefully."

In this example, the highlightText function checks if any of the values are in the keywords array. If they are, it wraps them in <strong> tags.

Using Tagged Template Literals for Styled Components

One of the most popular uses of tagged template literals is in styling components, especially in libraries like styled-components.

// Example using styled-components
import styled from 'styled-components';

const Button = styled.button`
    background-color: ${props => props.primary ? 'blue' : 'gray'};
    color: white;
    padding: 10px 20px;
    border: none;
    border-radius: 5px;
`;

// Usage
<Button primary>Primary Button</Button>
<Button>Secondary Button</Button>

In this example, styled.button is a tagged template literal that allows you to define CSS styles and interpolate values based on props.

Common Pitfalls and Best Practices

While tagged template literals are powerful, there are a few things to keep in mind to avoid common pitfalls:

  • Escaping Values: Always ensure that values are properly escaped to prevent security issues like XSS in web applications.
  • Complexity: Avoid overly complex tag functions. Keeping them focused on a single task makes them easier to maintain and test.
  • Performance: Be mindful of performance, especially in loops or performance-critical code sections. Tag functions can add overhead.

Real-World Applications

Tagged template literals find applications in various areas:

  • Localization: Dynamic generation of localized strings based on user settings.
  • Template Engines: Creating template engines for HTML or other markup languages.
  • Query Builders: Simplifying complex query string constructions.
  • Styled Components: As mentioned, styling components in frameworks like React.

Conclusion

Tagged template literals are a versatile feature in JavaScript that can greatly enhance your ability to manipulate and format strings. By understanding and utilizing them effectively, you can write cleaner and more maintainable code, especially in complex applications.

Feel free to experiment with tagged template literals and explore new ways to use them in your projects.

Additional Resources

By leveraging tagged template literals, you can take your JavaScript string manipulation to the next level and write more efficient and secure code.