When Should JS Tagged Templates Be Used?

Interactive examples showing what tagged templates receive, how they differ from string replacement, and where they become useful for structured dynamic data.

1. A Normal Template String vs. a Tagged Template

Code:
const author = 'zhangxinxu';

function tag(arr, exp) {
  return `${arr[0]}${exp}`;
}

const normal = `write by ${author}`;
const tagged = tag`write by ${author}`;

console.log(normal);
console.log(tagged);
Result:

      

2. What the Tag Function Receives

Code:
const author = 'zhangxinxu';

function inspectTag(arr, exp1, exp2) {
  return {
    arr0: arr[0],
    arr1: arr[1],
    exp1,
    exp2
  };
}

const parts = inspectTag`write by ${author}, welcome to share!`;

console.log(parts);
Result:

3. A replace() Function Can Imitate the Idea

Code:
const author = 'zhangxinxu';

function replaceTemplate(str) {
  return str.replace(/([\w\W]+)\$\{(\w+)\}([\w\W]+)/, function (matches, $1, $2, $3) {
    const scope = { author };
    return $1 + scope[$2] + $3;
  });
}

const result = replaceTemplate('write by ${author}, welcome to share!');

console.log(result);
Result:

      

4. Known Structure, Dynamic Processing: Invitation Text

Code:
const data = {
  name: 'Deng Tie',
  sex: 0,
  role: 1,
  time: 1640678098887
};

const invite = function (arrs, nameExp, sexExp, roleExp, timeExp) {
  let strName = nameExp;
  let strSex = ['Mr.', 'Ms.'][sexExp];

  const role = {
    '1': 'contestant',
    '2': 'judge',
    '3': 'scorekeeper',
    '4': 'photographer'
  };

  let strRole = role[roleExp];
  let strTime = new Date(timeExp).toLocaleDateString(undefined, {
    year: 'numeric',
    month: 'long',
    day: 'numeric'
  });

  let output = [arrs[0]];

  [strName, strSex, strRole, strTime].forEach((str, index) => {
    output.push(str, arrs[index + 1] || '');
  });

  return output.join('');
};

let content = invite`Sincerely invite ${data.name} ${data.sex} to participate as a ${data.role} on ${data.time} in the Shanghai Zhangjiang Cup Fishing Competition.
Organizer: Shanghai Pudong Fishing Association`;

console.log(content);
Result:

5. HTML Tagged Template with Event Binding

Code:
const render = function (data, container) {
  container.innerHTML = '';
  container.append(element(data));
};

const html = function (arr, ...keys) {
  let result = [arr[0]];

  keys.forEach(function (key, i) {
    if (typeof key == 'function') {
      result.push(i, arr[i + 1]);
    } else {
      result.push(key, arr[i + 1]);
    }
  });

  let template = document.createElement('template');
  template.innerHTML = result.join('');

  template.content.querySelectorAll('*').forEach(node => {
    let attrs = node.attributes;

    for (let i = attrs.length - 1; i >= 0; i--) {
      let attr = attrs[i].name;
      let value = attrs[i].value;

      if (/^on[a-z]+$/i.test(attr) && !isNaN(parseFloat(value))) {
        node.removeAttribute(attr);
        node.addEventListener(attr.replace(/^on/, ''), keys[Number(value)]);
      }
    }
  });

  return template.content;
};

let todos = ['Eat', 'Sleep', 'Beat Doudou'];

function addTodo() {
  todos.push(document.querySelector('#task').value);
  render(todos, document.querySelector('#app'));
}

let element = function (todos) {
  return html`

Task list (${todos.length})

    ${todos.map(todo => `
  • ${todo}
  • `).join('')}
`; }; render(todos, document.querySelector('#app'));
Result:

6. A Small DSL: URL Escaping

Code:
function url(arr, ...values) {
  let result = [arr[0]];

  values.forEach((value, index) => {
    result.push(encodeURIComponent(value), arr[index + 1]);
  });

  return result.join('');
}

const keyword = 'tagged templates & DSL';
const page = 2;

const api = url`/search?q=${keyword}&page=${page}`;

console.log(api);
Result:
Escaped URL