Templating Values

A Markup template is simply a JavaScript template literal, which means you can inject any value anywhere in the template.

const type = "button";
const label = "click me";

html`<button type="${type}">${label}</button>`;
// renders <button type="button">click me</button>

You can place values anywhere before, after and inside tags as well as inside attribute value quotes. However, the template will ignore any value placed on the tag body.

const type = "type='button'";

// will not render the type attribute
html`<button ${type}>click me</button>`;
// renders <button>click me</button>

This is because Markup parses the HTML string into nodes and will not allow dynamic tag or attribute names.

const tag = "button";

html`<${tag}>click me</${tag}>`;
// renders &lt;button&gt;click me&lt;/button&gt;

If you need dynamic elements, you can create one using element utility and add it to the template.

Optional quotes

If you want to inject a single value as attribute, you may choose to leave out the quotes altogether.

const type = "button";
const label = "click me";

html`<button type=${type} disabled=${disabled} class="btn ${cls}">${label}</button>`;

The above example set the type and disabled attributes without using the quotes because it used the template literal curly braces ${...}. This is find as long as the attribute is single value. For multi or mixed value like the class in above example, you need the quotes.

If in doubt or just for consistency’s sake, you can always go for keeping the quotes which matches the normal HTML syntax. Just know that this is an option.

Different values handling

Markup templates handle certain values in a unique way so lets explore all possible value you might use.

Primitive JavaScript values

Any primitive value will be kept as is in it string format. Primitives is the simplest values you can use and have no special handling by the template.

true => "true"
12 => "12"

Array

Array has special handling by the template. When you provide an array to the template to render, as long as it is not inside an attribute value quotes, it will have each item rendered separately on the DOM.

const arr = [1, 2, 3, 2]

html`${arr}`;
// renders: 1232

How each item in the array gets rendered depend on its type.

This simple capability makes it pretty easy to render list of anything, including smaller template elements.

const arr = [1, 2, 3, 2].map(n => html`<strong>item ${n}</strong>`)

html`${arr}`

Markup will update each "sub-template" according to the data in the array changes. Depending on the data change, Markup will re-order, add or remove nodes matching the data in the array.

Non-Primitive JavaScript values

With the exception of Array, every JavaScript object will be changed to its string equivalent. If the object contains a toString method the result of that will be displayed otherwise it falls back to default JavaScript handling:

{} => [object Object]
Map => [object Map]
Set => [object Set]

Nesting templates

Naturally, you can also place other HTMLTemplate instances inside your template which results in nested templates. It will be handled as expected and rendered just fine.

const button = html`<button type="button">click me</button>`;

let count = 0;
const countUp = html`count: ${count} ${button}`;
// renders:
// count: 0 <button type="button">click me</button>

One important thing to know is that even if you nest template, each one is tracked separately.

This means that if there is a change, only the template dependent on the change will get update which makes composing complex views really performant. This will make more sense once you start working with state and dynamic values.

DOM Elements

Any DOM element placed in the template will be rendered normally.

const button = document.createElement('button');

button.textContent = "click me"

let count = 0;
const countUp = html`count: ${count} ${button}`;
// renders:
// count: 0 <button>click me</button>

HTML string

If you just want to render raw HTML string in the DOM so people can see the tags, simply add them.

const htmlCode = '<span>HTML span tag</span>'

html`${htmlCode}`

The above html code will not get parsed and will display as is as text.

The only way to render HTML is to use the html to create HTMLTemplates. Everything else defaults to being just string.

If you want to render an HTML string inside the template you can use html in a different way. Simply call it and provide an array of string values.

const htmlCode = '<span>HTML span tag</span>'

html`${html([htmlCode])}`

The above example will parse the HTML string into DOM Nodes.

Please do NOT do this with HTML strings from untrusted sources like user input or third-party API call response. It can lead to HTML Injection Attack which is a security risk.

Nil values

Any nil value (null or undefined) will be rendered as string. This means that you should change them to empty string if you dont want to render any nil value

html`${null}` // renders "null"
html`${undefined}` // renders "undefined"

Functions

Functions in the template also receive special treatment. Basically, there are two reasons to add functions to the template.

  1. Event handlers: To listen to events on tags.
  2. Dynamic values: To react to value changes anywhere in the DOM.

Please explore those two topics to understand more.

Web Components

Template values are handled differently when used with web component attributes.

If you use Web Components tag in your template the Markup will automatically detect it and try to pass the values directly to the component as is in case the component itself has a property or setter for that value.

Let's take for example the following TodoItem web component:

class TodoItem extends HTMLElement {
  static observedAttributes = ["todo"];
  todo: Todo = null; // will be updated by the template
}

customElements.define('todo-item', TodoItem)

When you try to pass data as props to a web component, the data will be passes as is directly to the property of the component and the attribute value will be JSON stringified.

const todo = {name: "go to gym"};

html`<todo-item todo="${todo}"/>`
// renders: 
// <todo-item todo="{&quot;name&quot;:&quot;go to gym&quot;}"></todo-item>

This is what makes this library perfect to work with web components as it enhances the developer experience and makes native web component much more appealing. learn more about working with Web Components