Template literals provide powerful string interpolation and multi-line strings with backtick syntax.
Basic Syntax#
1// Template literal with backticks
2const greeting = `Hello, World!`;
3
4// String interpolation
5const name = 'John';
6const message = `Hello, ${name}!`;
7console.log(message); // 'Hello, John!'
8
9// Expressions in interpolation
10const a = 5;
11const b = 10;
12console.log(`Sum: ${a + b}`); // 'Sum: 15'
13console.log(`Product: ${a * b}`); // 'Product: 50'
14console.log(`Bigger: ${a > b ? a : b}`); // 'Bigger: 10'
15
16// Function calls
17const upper = (s) => s.toUpperCase();
18console.log(`Hello, ${upper(name)}!`); // 'Hello, JOHN!'Multi-line Strings#
1// Multi-line without concatenation
2const multiLine = `
3 This is a
4 multi-line
5 string
6`;
7
8// Preserves whitespace and newlines
9const html = `
10<div class="card">
11 <h2>Title</h2>
12 <p>Content</p>
13</div>
14`;
15
16// Avoid leading whitespace with trim
17const trimmed = `
18 Line 1
19 Line 2
20`.trim();
21
22// Or use dedent pattern
23function dedent(str) {
24 const lines = str.split('\n');
25 const indent = lines[1]?.match(/^\s*/)?.[0].length || 0;
26 return lines
27 .map(line => line.slice(indent))
28 .join('\n')
29 .trim();
30}Nested Templates#
1// Templates can be nested
2const items = ['Apple', 'Banana', 'Orange'];
3
4const list = `
5<ul>
6 ${items.map(item => `<li>${item}</li>`).join('\n ')}
7</ul>
8`;
9
10// Conditional content
11const user = { name: 'John', admin: true };
12
13const profile = `
14<div class="profile">
15 <h2>${user.name}</h2>
16 ${user.admin ? `<span class="badge">Admin</span>` : ''}
17</div>
18`;
19
20// Complex nesting
21const data = {
22 title: 'Products',
23 categories: [
24 { name: 'Electronics', items: ['Phone', 'Laptop'] },
25 { name: 'Clothing', items: ['Shirt', 'Pants'] }
26 ]
27};
28
29const catalog = `
30<h1>${data.title}</h1>
31${data.categories.map(cat => `
32 <section>
33 <h2>${cat.name}</h2>
34 <ul>
35 ${cat.items.map(item => `<li>${item}</li>`).join('')}
36 </ul>
37 </section>
38`).join('')}
39`;Tagged Templates#
1// Tag function receives parts and values
2function highlight(strings, ...values) {
3 return strings.reduce((result, str, i) => {
4 const value = values[i] !== undefined
5 ? `<mark>${values[i]}</mark>`
6 : '';
7 return result + str + value;
8 }, '');
9}
10
11const name = 'JavaScript';
12const result = highlight`Learn ${name} today!`;
13// 'Learn <mark>JavaScript</mark> today!'
14
15// Tag function parameters
16function tag(strings, ...values) {
17 console.log(strings); // Array of string parts
18 console.log(values); // Array of interpolated values
19 console.log(strings.raw); // Raw strings (escape sequences not processed)
20}
21
22tag`Hello\nWorld ${1} and ${2}`;
23// strings: ['Hello\nWorld ', ' and ', '']
24// values: [1, 2]
25// strings.raw: ['Hello\\nWorld ', ' and ', '']HTML Escaping#
1// Prevent XSS with tagged template
2function html(strings, ...values) {
3 const escape = (str) =>
4 String(str)
5 .replace(/&/g, '&')
6 .replace(/</g, '<')
7 .replace(/>/g, '>')
8 .replace(/"/g, '"')
9 .replace(/'/g, ''');
10
11 return strings.reduce((result, str, i) => {
12 const value = values[i] !== undefined ? escape(values[i]) : '';
13 return result + str + value;
14 }, '');
15}
16
17const userInput = '<script>alert("xss")</script>';
18const safe = html`<div>${userInput}</div>`;
19// '<div><script>alert("xss")</script></div>'
20
21// With raw HTML support
22function safeHtml(strings, ...values) {
23 return strings.reduce((result, str, i) => {
24 let value = values[i];
25 if (value === undefined) return result + str;
26
27 // Allow marked-safe values
28 if (value && value.__safe) {
29 value = value.value;
30 } else {
31 value = escape(String(value));
32 }
33 return result + str + value;
34 }, '');
35}
36
37const safe = (value) => ({ __safe: true, value });CSS-in-JS Pattern#
1// Styled component pattern
2function css(strings, ...values) {
3 return strings.reduce((result, str, i) => {
4 const value = values[i] !== undefined ? values[i] : '';
5 return result + str + value;
6 }, '');
7}
8
9const color = 'blue';
10const styles = css`
11 .button {
12 background-color: ${color};
13 padding: 10px 20px;
14 border-radius: 4px;
15 }
16`;
17
18// With props
19function styled(strings, ...fns) {
20 return function(props) {
21 return strings.reduce((result, str, i) => {
22 const value = fns[i] ? fns[i](props) : '';
23 return result + str + value;
24 }, '');
25 };
26}
27
28const buttonStyle = styled`
29 background: ${props => props.primary ? 'blue' : 'gray'};
30 color: ${props => props.primary ? 'white' : 'black'};
31`;
32
33console.log(buttonStyle({ primary: true }));SQL Query Builder#
1// Safe SQL queries
2function sql(strings, ...values) {
3 const query = strings.reduce((result, str, i) => {
4 return result + str + (i < values.length ? `$${i + 1}` : '');
5 }, '');
6
7 return {
8 text: query,
9 values: values
10 };
11}
12
13const userId = 123;
14const status = 'active';
15
16const query = sql`
17 SELECT * FROM users
18 WHERE id = ${userId}
19 AND status = ${status}
20`;
21
22// { text: 'SELECT * FROM users WHERE id = $1 AND status = $2',
23// values: [123, 'active'] }
24
25// Execute with parameterized query
26// db.query(query.text, query.values);Internationalization#
1// i18n tagged template
2const translations = {
3 en: {
4 'greeting': 'Hello, {0}!',
5 'items': 'You have {0} items'
6 },
7 es: {
8 'greeting': '¡Hola, {0}!',
9 'items': 'Tienes {0} artículos'
10 }
11};
12
13function createI18n(locale) {
14 return function i18n(strings, ...values) {
15 const key = strings.join('{?}').replace(/\{\?\}/g, (_, i) => `{${i}}`);
16 let template = translations[locale][key] || key;
17
18 values.forEach((value, i) => {
19 template = template.replace(`{${i}}`, value);
20 });
21
22 return template;
23 };
24}
25
26const t = createI18n('es');
27console.log(t`greeting ${'Juan'}`); // '¡Hola, Juan!'
28console.log(t`items ${5}`); // 'Tienes 5 artículos'Debug Logging#
1// Debug tagged template
2function debug(strings, ...values) {
3 const timestamp = new Date().toISOString();
4 const message = strings.reduce((result, str, i) => {
5 const value = values[i] !== undefined
6 ? JSON.stringify(values[i])
7 : '';
8 return result + str + value;
9 }, '');
10
11 console.log(`[${timestamp}] ${message}`);
12 return message;
13}
14
15const user = { id: 1, name: 'John' };
16debug`User logged in: ${user}`;
17// [2024-01-15T10:30:00.000Z] User logged in: {"id":1,"name":"John"}
18
19// Conditional debug
20const DEBUG = process.env.NODE_ENV !== 'production';
21
22function log(strings, ...values) {
23 if (!DEBUG) return;
24 // ... logging logic
25}Regular Expressions#
1// Build complex regex with comments
2function regex(strings, ...values) {
3 const pattern = strings.raw
4 .reduce((result, str, i) => {
5 const value = values[i] !== undefined ? values[i] : '';
6 return result + str + value;
7 }, '')
8 .replace(/\s+#.*/gm, '') // Remove comments
9 .replace(/\s+/g, ''); // Remove whitespace
10
11 return new RegExp(pattern);
12}
13
14const emailRegex = regex`
15 ^ # Start
16 [a-zA-Z0-9._%+-]+ # Username
17 @ # @ symbol
18 [a-zA-Z0-9.-]+ # Domain
19 \. # Dot
20 [a-zA-Z]{2,} # TLD
21 $ # End
22`;
23
24console.log(emailRegex.test('test@example.com')); // trueString.raw#
1// String.raw preserves escape sequences
2console.log(`Line1\nLine2`);
3// Line1
4// Line2
5
6console.log(String.raw`Line1\nLine2`);
7// Line1\nLine2
8
9// Useful for regex and paths
10const path = String.raw`C:\Users\John\Documents`;
11console.log(path); // C:\Users\John\Documents
12
13// Custom raw tag
14function latex(strings) {
15 return strings.raw.join('');
16}
17
18const formula = latex`\frac{1}{2}`;
19console.log(formula); // \frac{1}{2}Best Practices#
Basic Usage:
✓ Use for string interpolation
✓ Use for multi-line strings
✓ Use for HTML/SQL templates
✓ Trim when whitespace matters
Tagged Templates:
✓ Use for escaping (HTML, SQL)
✓ Use for DSLs (CSS-in-JS)
✓ Use for i18n
✓ Document tag function behavior
Performance:
✓ Tagged templates are cached
✓ Avoid complex logic in interpolations
✓ Use for clarity, not everywhere
✓ Consider template caching for repeated use
Avoid:
✗ Overusing for simple strings
✗ Deep nesting without reason
✗ Forgetting to escape user input
✗ Complex expressions in ${}
Conclusion#
Template literals simplify string interpolation with ${} syntax and enable multi-line strings. Tagged templates unlock powerful patterns like HTML escaping, CSS-in-JS, SQL query builders, and i18n. Use String.raw when you need literal backslashes. Always escape user input in HTML contexts to prevent XSS vulnerabilities.