Back to Blog
JavaScriptTemplate LiteralsES6Strings

JavaScript Template Literals Guide

Master JavaScript template literals for string interpolation and tagged templates.

B
Bootspring Team
Engineering
July 27, 2018
6 min read

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, '&amp;') 6 .replace(/</g, '&lt;') 7 .replace(/>/g, '&gt;') 8 .replace(/"/g, '&quot;') 9 .replace(/'/g, '&#39;'); 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>&lt;script&gt;alert(&quot;xss&quot;)&lt;/script&gt;</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')); // true

String.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.

Share this article

Help spread the word about Bootspring