Getting Started with Template Literals In JavaScript
Updated on · 8 min read|Template literals, also known as template strings, are a powerful feature introduced in ECMAScript 2015 (ES6) that allows for more flexible and expressive string formatting in JavaScript. Unlike traditional string literals enclosed in single or double quotes, template literals are enclosed in backticks and support interpolation and multiline strings.
These literals have become an essential tool for developers, enabling them to write cleaner and more readable code. In this post, we will explore the mechanics of template literals, their syntax, and demonstrate their various use cases in JavaScript programming.
Basic syntax and usage
Historically, JavaScript strings were limited to single and double quotation marks, which could not span across multiple lines without explicit concatenation. Developers were forced to use the plus (+
) operator or the concat() method to create lengthy or dynamically constructed strings. This often resulted in code that was cumbersome and difficult to read, especially when the strings incorporated variables or when formatting was crucial. For example, constructing a sentence or a block of HTML with mixed content, such as variables or user input, could quickly become a complex task, riddled with potential errors.
Template literals in JavaScript offer a straightforward syntax that differs from traditional string literals. They are enclosed within backticks (`), allowing for more flexibility and functionality compared to single or double quotes. The introduction of this feature has significantly reduced the complexity associated with string concatenation and manipulation, practices that were once cumbersome and a common source of errors.
To create a basic template literal, simply enclose the desired string content within backticks. For example:
javascriptconst name = "John"; const greeting = `Hello, ${name}!`; console.log(greeting); // Output: Hello, John!
javascriptconst name = "John"; const greeting = `Hello, ${name}!`; console.log(greeting); // Output: Hello, John!
In this example, the ${name}
syntax is used for string interpolation, allowing the variable name to be dynamically inserted into the string.
Template literals also support multiline strings without the need for explicit newline characters. For instance:
javascriptconst multilineString = ` This is a multiline string. It can span multiple lines without the need for explicit newline characters. `; console.log(multilineString); // Output: This is a multiline string. // It can span multiple lines // without the need for explicit newline characters.
javascriptconst multilineString = ` This is a multiline string. It can span multiple lines without the need for explicit newline characters. `; console.log(multilineString); // Output: This is a multiline string. // It can span multiple lines // without the need for explicit newline characters.
This feature simplifies the creation of complex strings, especially for scenarios involving multiline text such as HTML templates or SQL queries.
Benefits of using template literals
Template literals offer several compelling advantages over the traditional string construction methods in JavaScript. These benefits not only lead to more readable and concise code but also to a more streamlined development process.
Improved readability
The clarity of code is significantly enhanced with template literals. The use of backticks and embedded expressions means that developers can write strings that are closer to natural language, which is particularly useful when the string content includes variables or expressions. The need for concatenation with the +
operator is radically reduced, if not eliminated, making templates much easier to read and understand at a glance.
Compared to the old approach that can look clunky and be more difficult to follow:
javascriptvar user = "Bob"; var message = "Hello, " + user + "! Welcome to our website!"; console.log(message); // Output: "Hello, Bob! Welcome to our website!"
javascriptvar user = "Bob"; var message = "Hello, " + user + "! Welcome to our website!"; console.log(message); // Output: "Hello, Bob! Welcome to our website!"
The template literal version is more concise and readable:
javascriptconst user = "Bob"; const message = `Hello, ${user}! Welcome to our website!`; console.log(message); // Output: "Hello, Bob! Welcome to our website!"
javascriptconst user = "Bob"; const message = `Hello, ${user}! Welcome to our website!`; console.log(message); // Output: "Hello, Bob! Welcome to our website!"
Multiline strings
One of the most notable improvements template literals bring to JavaScript is the ability to create multiline strings effortlessly. Previously, developers had to include newline characters (\n
), and use concatenation or array joins to manage multiple lines in a string. Now, text can be spread over several lines within the backticks, with the resulting string preserving the intended line breaks, greatly simplifying the creation of formatted text blocks.
Before ES6, creating a multiline string could look something like this:
javascriptvar address = "Street: " + street + "\n" + "City: " + city + "\n" + "Zip Code: " + zipCode;
javascriptvar address = "Street: " + street + "\n" + "City: " + city + "\n" + "Zip Code: " + zipCode;
With template literals, this becomes more natural and easier to manage:
javascriptconst address = `Street: ${street} City: ${city} Zip Code: ${zipCode}`; console.log(address); // Output: // Street: 123 Main St // City: Anytown // Zip Code: 12345
javascriptconst address = `Street: ${street} City: ${city} Zip Code: ${zipCode}`; console.log(address); // Output: // Street: 123 Main St // City: Anytown // Zip Code: 12345
Expression interpolation
Perhaps the most powerful feature of template literals is expression interpolation. Within a template literal, any valid JavaScript expression, when surrounded by ${}
syntax, is evaluated, and the resulting value is seamlessly integrated into the string. This makes complex constructions, such as including calculated values, inserting variables, or even function calls within a string, much more straightforward and less error-prone than concatenating multiple strings together.
Consider creating a full sentence that includes a mathematical operation:
javascriptvar price = 19.99; var taxRate = 0.08; var total = price + price * taxRate; var message = "The total price is: $" + total.toFixed(2);
javascriptvar price = 19.99; var taxRate = 0.08; var total = price + price * taxRate; var message = "The total price is: $" + total.toFixed(2);
With template literals, the entire operation can be neatly placed within the string:
javascriptconst price = 19.99; const taxRate = 0.08; const message = `The total price is: $${(price + price * taxRate).toFixed(2)}`; console.log(message); // Output: "The total price is: $21.59"
javascriptconst price = 19.99; const taxRate = 0.08; const message = `The total price is: $${(price + price * taxRate).toFixed(2)}`; console.log(message); // Output: "The total price is: $21.59"
Expression interpolation is more than a convenience — it brings about a qualitative change in writing JavaScript code. It allows for the composition of dynamic strings with an elegance and simplicity that was not previously possible, and this interpolation is done in a way that is automatically secure. Special characters within expressions are properly escaped, reducing the risk of injection attacks, a common concern in web development.
These benefits collectively serve to make code not only cleaner and more maintainable but also more secure and robust. As a result, template literals are seen as an indelible part of modern JavaScript coding practices, embraced by the development community for their versatility and user-friendliness.
Advanced use cases and features
Template literals offer a range of advanced features that extend their functionality beyond simple string interpolation. These features include tagged template literals, raw strings, and the ability to nest template literals within other template literals. These capabilities provide developers with a powerful toolset for creating complex and dynamic strings in JavaScript.
Tagged template literals
Beyond simple interpolation, tagged template literals offer an advanced feature where a function processes a template literal. The tag, which is a function name placed before the template literal, receives the string parts and expressions as arguments:
javascriptfunction highlight(strings, ...values) { return strings.reduce((result, string, i) => { return `${result}${string}<strong>${values[i] || ""}</strong>`; }, ""); } const name = "JavaScript"; const version = "ES6"; const sentence = highlight`Learning ${name} with its new features in ${version} can be exciting.`; console.log(sentence); // Output: "Learning <strong>JavaScript</strong> with its new features in <strong>ES6</strong> can be exciting."
javascriptfunction highlight(strings, ...values) { return strings.reduce((result, string, i) => { return `${result}${string}<strong>${values[i] || ""}</strong>`; }, ""); } const name = "JavaScript"; const version = "ES6"; const sentence = highlight`Learning ${name} with its new features in ${version} can be exciting.`; console.log(sentence); // Output: "Learning <strong>JavaScript</strong> with its new features in <strong>ES6</strong> can be exciting."
Tag functions like highlight
can be used for various purposes, such as sanitizing input, localization, styled components, and more. They provide a customizable parsing method that can interpret the parts of a template literal in unique and powerful ways. Their utility extends into TypeScript, where they can be used to create custom string literal types.
For more details on tagged template literals, see the post Advanced String Manipulation with Tagged Templates In JavaScript and MDN Web Docs.
Raw string access with template literals
Template literals provide access to unescaped strings through the raw
property, which is a feature of the first argument passed to a tag function when working with tagged template literals. This allows developers to handle strings that include escape characters without processing them.
The raw strings can be particularly useful in scenarios where content should not be interpreted, such as regular expressions or when working with domain-specific languages within a JavaScript context. Here's an example of how the raw
property can be leveraged:
javascriptfunction showRaw(strings) { const rawString = strings.raw[0]; console.log(rawString); } showRaw`This is a text with a newline character \n and a tab \t.`; // Output: "This is a text with a newline character \n and a tab \t."
javascriptfunction showRaw(strings) { const rawString = strings.raw[0]; console.log(rawString); } showRaw`This is a text with a newline character \n and a tab \t.`; // Output: "This is a text with a newline character \n and a tab \t."
In the example above, the typical interpretation of \n
and \t
as newline and tab characters respectively is not performed. Instead, the literal backslashes are maintained in the raw output.
Raw strings ignore all escape sequences and treat backslashes as simple text characters. This is how you would use raw strings to define a regular expression without doubling backslashes:
javascript// Pattern for matching a number with two decimal places const regexPattern = String.raw`^\d+\.\d{2}$`; const regex = new RegExp(regexPattern); console.log(regex.test("123.45")); // Output: true
javascript// Pattern for matching a number with two decimal places const regexPattern = String.raw`^\d+\.\d{2}$`; const regex = new RegExp(regexPattern); console.log(regex.test("123.45")); // Output: true
Typically, in a standard string literal, developers would need to double every backslash to escape them, which can be cumbersome and error-prone, especially in complex patterns:
javascriptconst regexPattern = "^\\d+\\.\\d{2}$"; // Double backslashes are needed const regex = new RegExp(regexPattern);
javascriptconst regexPattern = "^\\d+\\.\\d{2}$"; // Double backslashes are needed const regex = new RegExp(regexPattern);
By utilizing raw strings, developers can write strings that are closer to their intended form, improving legibility and reducing the risk of introducing errors that escape sequences might cause.
Nesting template literals within one another
An often overlooked feature of template literals is their ability to be nested. This allows developers to create complex strings by embedding a template literal inside another, which can be useful for constructing hierarchical or conditional text structures.
Nesting template literals becomes particularly advantageous when dealing with multiple layers of data or when creating strings based on certain conditions. Here's a simple example of nesting template literals:
javascriptconst user = { name: "Bob", details: { age: 25, location: "London", }, }; const greeting = `Hello, ${user.name}! ${ user.details.age ? `You are ${user.details.age} years old. ` : "" }Welcome to ${user.details.location}.`; console.log(greeting); // Output: "Hello, Bob! You are 25 years old. Welcome to London."
javascriptconst user = { name: "Bob", details: { age: 25, location: "London", }, }; const greeting = `Hello, ${user.name}! ${ user.details.age ? `You are ${user.details.age} years old. ` : "" }Welcome to ${user.details.location}.`; console.log(greeting); // Output: "Hello, Bob! You are 25 years old. Welcome to London."
In this example, a conditional check is performed inside the nested template literal to determine if the user's age should be included in the greeting.
Nesting template literals can also be used in more complex scenarios, such as in generating HTML structures or when working with component-based frameworks where the content may depend on multiple levels of data passed as properties or state:
javascriptconst shoppingList = ["Apples", "Oranges", "Milk"]; const shoppingListHtml = ` <ul> ${shoppingList.map((item) => `<li>${item}</li>`).join("")} </ul> `; console.log(shoppingListHtml); // Output: // <ul> // <li>Apples</li> // <li>Oranges</li> // <li>Milk</li> // </ul>
javascriptconst shoppingList = ["Apples", "Oranges", "Milk"]; const shoppingListHtml = ` <ul> ${shoppingList.map((item) => `<li>${item}</li>`).join("")} </ul> `; console.log(shoppingListHtml); // Output: // <ul> // <li>Apples</li> // <li>Oranges</li> // <li>Milk</li> // </ul>
In the above HTML generation example, a template literal is used within a .map()
method callback and then embedded within another template literal to form the complete HTML list structure.
Nesting facilitates the construction of dynamic strings from sub-components, allowing each sub-component to be independently managed and composed. The seamless integration of nested template literals effectively minimizes the complexity and improves the maintainability of the code, making it a powerful pattern in JavaScript's string manipulation capabilities.
Security considerations
Alongside their syntactic convenience, template literals also bring some security benefits that are particularly relevant in web development. When using template literals, special characters in variables or expressions are automatically escaped, reducing the risk of common web vulnerabilities such as cross-site scripting (XSS) attacks.
However, it is important to note that template literals in themselves do not prevent all security risks. For instance, when creating strings from user input, developers must still be careful to sanitize and validate the input to prevent security issues. Here's an example of how template literals can be used safely:
javascriptconst userProvidedInput = "<script>alert('Hacked!');</script>"; const message = `User input was: ${userProvidedInput}`; console.log(message); // Potentially dangerous if rendered in HTML without proper escaping
javascriptconst userProvidedInput = "<script>alert('Hacked!');</script>"; const message = `User input was: ${userProvidedInput}`; console.log(message); // Potentially dangerous if rendered in HTML without proper escaping
To enhance security, developers should apply the principles of input validation and output encoding. While the use of template literals ensures that the string literal itself is treated as literal text, any HTML rendering still needs to consider the security context:
javascriptconst userProvidedInput = "<script>alert('Hacked!');</script>"; const safeInput = sanitizeUserInput(userProvidedInput); // A function that sanitizes input const message = `User input was: ${safeInput}`; // Now, rendering `message` is safer because `safeInput` is sanitized.
javascriptconst userProvidedInput = "<script>alert('Hacked!');</script>"; const safeInput = sanitizeUserInput(userProvidedInput); // A function that sanitizes input const message = `User input was: ${safeInput}`; // Now, rendering `message` is safer because `safeInput` is sanitized.
Even though JavaScript does not automatically sanitize dynamic content within template literals, it does make the job of output encoding easier by avoiding accidental syntax errors caused by quotes in strings. Ultimately, the use of template literals, coupled with diligent sanitization practices, can contribute to more secure JavaScript applications.
Conclusion
The introduction of template literals has significantly overhauled the way strings are created and manipulated in JavaScript, offering an impressive range of capabilities that extend beyond mere aesthetics. With features such as improved readability through clear syntax, support for multiline strings, and the power of expression interpolation, template literals have become an indispensable tool in modern JavaScript development.
The practical applications of template literals, such as creating dynamic strings, embedding expressions, and utilizing tagged templates, have illustrated their flexibility and effectiveness in various coding scenarios. The ability to nest template literals has introduced a new level of composability to string manipulation, enabling developers to build complex strings in an elegant and maintainable manner.