lit-html rendering implementation
Tags: javascript
Published: 2019-10-12 17:37:36

_In a world of dominant large UI frameworks and libraries, a simple solution is attempting to combine the ergonomics of the existing technologies and the power of the modern web standards._

The aim of this article is to give you an insight of the core concepts of lit-html's rendering process. But, before that:

## What is lit-html?

_Note: In case you are familiar with lit-html, you can skip this section._

lit-html is a templating library which makes use of the built-in browser HTML parser rather than incorporating standalone one. Internally it creates `<template>` elements from user-defined string literals, and inserts and/or updates the data provided on `render` where needed. This makes the library not only a good performer but also extremely small!

### Template literals and tagged templates

Before moving to the core part of this article, it is important to cover the not-so-popular tagged template literals which are a more advanced form of template literals. The functionality allows the user to inspect the different parts of a template literal -- the static string parts and the interpolated data. The tag itself is represented as a function:

```typescript
function hello(strings, name, surname) {
  return {
    strings,
    name,
    surname
  };
}

const name = 'John';
const surname = 'Doe'

const obj = hello`Hello, ${name} ${surname}!`;

console.log(obj);

// Output:
// {
//    name: 'John',
//    surname: 'Doe',
//    strings: [ 'Hello, ', '!' ]
// }
```
Note that the tag function (template tag) doesn't neccesarily return a string. In our case, we return an object with the tag function input data.

### A simple template

Now, that we have basic understanding of what lit-html is and how tagged templates work, let's create a small test template for the sake of consistency. First, we should import the `html` template tag from `lit-html`. Then, we can write a function that returns a template literal which will represent the lit-html template we want.

```javascript
import { html } from 'lit-html';

const badge = (title) => html`
  <div class="badge">
    <p>${title}</p>
  </div>
`;
```

_Note: lit-html supports SVG templates as well via the `svg` tag_

Last, we would like to render the template somewhere. For this purpose, we will have to import one more function called `render`, again from `lit-html`. As its name indicates, it should help us display our template on the screen:

```javascript

import { html, render } from 'lit-html';

//...

render(badge('Admin'), document.body);
```

The function itself accepts a template and a container as its first two arguments. After execution we should have our admin badge added in the body of the page. Simple, isn't it? Okay, let's take a look how this works behind the scenes.

### Further information

If you are interested in expanding your lit-html knowledge before learning about rendering, you can take a look at these:

- [GitHub repos list app](https://codesandbox.io/s/m5lkpjm5mj) online demo by [Martin Hochel](https://github.com/Hotell)
- [Article example](https://codesandbox.io/embed/eloquent-solomon-n9fbk) online demo
- [Official lit-html docs](https://lit-html.polymer-project.org/guide)

## Rendering implementation

_Disclaimer: The article is written based on lit-html v1.1_

We already learnt how we can write a simple lit-html templates via the `html` template tag and the `render` function. Now we will explore the internals of the library. Note that we won't cover all of the details but the core concepts. The idea is to get an insight how this thing runs. In the process we will include code snippets of the different phases which are taken from lit-html source code. However, they are greatly simplified, so be advised.

We can informally separate the process in three parts:

1. Preparation - `html` template tag and `render` function
2. Template processing - creation of `<template>` & lit-html `Template`
3. Creating a template instance - `TemplateInstance` & cloning

Let's get started!

### 1. Preparation

In the very beginning, let's explore what the `html` template tag does. We will introduce the `TemplateResult` which is nothing more but a wrapper of the static string parts and the values from the tag function. Additionally, it keeps a reference to a `TemplateProcessor` and it has a method which generates a `<template>` called `getTemplateElement`. We will cover these two later. So, what lit-html does with `html` template tag is to simply create a new instance of the `TemplateResult`. All of this can be summarized in this code snippet:

```javascript
class TemplateResult {
  strings: ReadonlyArray<string>;
  values: ReadonlyArray<unknown>;
  processor: TemplateProcessor;

  constructor(strings, values, processor) { /* ... */ }

  getTemplateElement(): HTMLTemplate { /* ... */ }
}

const defaultTemplateProcessor = /* ... */

function html(strings, value): TemplateResult {
  return new TemplateResult(strings, values, defaultTemplateProcessor);
}
```
1. [_Source of `TemplateResult`_](https://github.com/Polymer/lit-html/blob/master/src/lib/template-result.ts#L51)
2. [_Source of `html` function_](https://github.com/Polymer/lit-html/blob/master/src/lit-html.ts#L65)

Following the steps that we used in the demo, a generated `TemplateResult` should be then passed to the `render` function. It looks like we are almost done, but actually most of the work starts from here.

Having a dive into `render` implementation, we will notice that it has access to a weak map which associates a render container with an object of type `NodePart`. It acts as a cache:

```typescript
const parts = new WeakMap();

function render(result: TemplateResult, container: Element | DocumentFragment) {
  let part = parts.get(container);

  if (part === undefined) {
    // *Clear container, if full*
    part = new NodePart(templateFactory);
    parts.set(container, part);
    part.appendInto(container);
  }

  part.setValue(result);
  part.commit();
}
```
[_Source of `render` function_](https://github.com/Polymer/lit-html/blob/master/src/lib/render.ts#L41)

Presumably, there should be a lot of questions. Let's start with what `NodePart` is. Well, `NodePart` or `Part` (the interface) represents a dynamic part of a template instance rendered by lit-html. Or in other words -- where our data is plugged.

As you can see in the code above, we append a Part into our container (e.g. in our demo case - the body). This happens only, if the container hasn't been used for rendering yet. If it was, the cache will already have a Part associated with it. It's interesting that if you take a look at the DOM tree at this step of the process, you will notice some empty HTML comments added to it. These are used as markers for the beginning and the end of the respective Part.

After we have our container prepared (i.e. with inserted Part), we set the `TemplateResult` as pending value to the respective Part. By `commit`-ing after that, the template processing triggers.

Moving forward, we will elaborate on `commit` and `templateFactory` in the next section.

_Note: The `WeakMap` will allow to have its values garbage-collected, if they aren't referenced anywhere in the code._

### 2. Template processing

In the first section we just mentioned about the `getTemplateElement` method of the `TemplateResult`. Here we will actually make use of it. What it does is simple -- join all static string parts of the template and add markers where we plan to plug data. In the end, return a `<template>`. lit-html uses different types of markers depending on the place of interpolation. For instance, the content of an element will be marked with a comment of `<!--{{lit-guid}}-->` type whereas an attribute -- with `ATTR_NAME$lit$="{{lit-guid}}"`. If we take the template we wrote in our demo above as an example, we will end up with something like this:

```html
<template>
  #document-fragment
  <div class="badge">
    <p><!--{{lit-9858251939913858}}--></p>
  </div>
</template>
```

Cool, isn't it?

Okay, nice. The next part of the chain is the `templateFactory` which we passed previously on our `NodePart`. It incorporates the Factory pattern with some caching as you might've already guessed from the name. The output -- a lit-html template or `Template`:

```typescript
class Template {
  parts: TemplatePart[] = [];
  element: HTMLTemplateElement;

  constructor(result: TemplateResult, template: HTMLTemplateElement) {
    this.element = template;
    while (partIndex < result.values.length) {
      // ...
      this.parts.push({ type: 'node', index });
      // ...
    }
  }
}

function templateFactory(result: TemplateResult) {
  // *Check if template is in cache. If not, create a new one*
  const t = new Template(result, result.getTemplateElement());

  // *Add to cache*

  return t;
}
```

1. [_Source of `Template`_](https://github.com/Polymer/lit-html/blob/master/src/lib/template.ts#L43)
2. [_Source of `templateFactory`_](https://github.com/Polymer/lit-html/blob/master/src/lib/template-factory.ts#L48)

What the `Template` does is to process the generated `<template>` from the `TemplateResult` by recording/tracking the positions of the markers that we talked about earlier. That way, our lit-html template is ready to be used.

Okay, let's go back to the `NodePart` and the commiting process we've been talking about.

It is important to mention that for the purposes of this article, we will cover only the process of commiting of a `TemplateResult`. You might already know that a Part can accept a node, an iterable or text as well.

```typescript
class NodePart implements Part {
  commit(value) {
    // ...
    this._commitTemplateResult(value);
    // ...
  }

  _commitTemplateResult(value) {
    // Create a Template
    const template = this.templateFactory(value);

    if (this.value instanceof TemplateInstance && this.value.template === template) {
      // *Update the instance*
    } else {
      // *Create the instance*
      this.value = /* new instance */
    }
  }
}
```
[_Source of `NodePart`_](https://github.com/Polymer/lit-html/blob/master/src/lib/parts.ts#L236)

As you can see this is where we make use of the template factory which should return a ready-to-use lit-html `Template`. After that we create or update the template instance associated with the `NodePart`.

### 3. Creating a template instance

It's time to create our `TemplateInstance`. The instance is an object
that accepts a lit-html `Template` and the processor we briefly mentioned in the first code snippet. Its task is to use the processor in order to create dynamic parts in the document fragment derived from the `<template>` during cloning:

```typescript
class TemplateInstance {
  private _parts: Array<Part|undefined> = [];
  processor: TemplateProcessor;
  template: Template;

  constructor(template: Template, processor: TemplateProcessor) { /* ... */ }

  update(values: ReadonlyArray<unknown>) {
    // *for each part*
    // *set a value and then commit*
  }

  clone(): DocumentFragment {
    // ...
    const fragment = this.template.element.content.cloneNode(true) as DocumentFragment;

    // *Use the processor and the Template's part metadata to create Parts*

    return fragment;
  }
}
```
[_Source of `TemplateInstance`_](https://github.com/Polymer/lit-html/blob/master/src/lib/template-instance.ts#L29)

The processor itself contains the lit-html template specific stuff like the attribute prefixes `.` `@` or `?`. Based on this syntax, it creates a Part -- `NodePart`, `AttributePart`, etc.

Finally, after we have our instance created and cloned, we can commit it which means that it is inserted to the DOM. At this point of the process you should be able to see the template rendered on the screen!

Now, on every new update, lit-html will use the exactly the instance and will modify only the values of the dynamic parts we created. Neat!

[**lit-html GitHub repository**](https://github.com/Polymer/lit-html)

### In short

- lit-html builds a HTML template by concatenating the static string parts and inserting markers where an interpolation is going to happen.
- Later, the locations of these markers are recorded in an lit-html template object.
- An instance is created. The metadata from the lit-html template is used in order to create dynamic parts within the template instance.
- Finally, the ready product is added to the DOM and the dynamic parts are being updated when new values are provided.

## Conclusion

While the templating with lit-html looks very similar to what we have with the popular web UI technologies, it is vastly different behind the scenes. The lack of an additional compilation step or the need of a virtual DOM, contribute to the simplicity of this templating library which has its own place in the modern and diverse front-end world.

*Enjoy this article? Head on over to [This Dot Labs](https://www.thisdotlabs.com) and check us out!  We are a tech consultancy that does all things javascript and front end.  We specialize in open source software like, Angular, React and Vue.*