A bit about lit-html rendering

So Zouhir has been asking me a bit about how rendering works with lit-html and custom elements base classes, so I decided it might be best to just write a bit here.

So for those who don't know, lit-html is a new mini library for generating and updating HTML content. From a user perspective, lit-html feels a whole lot like JSX, but it's all standard JavaScript and no compile step is required!

lit-html uses "tagged string template literals" from ES2015, and you define your markup using the html`` tag.

lit-html is very JSX like but uses regular JavaScript

The cool thing about string template literals is that, for the same tagged template string, they return the same strings and only the values will differ:

This allows lit-html to return a TemplateResult object, which can be rendered to the DOM. TemplateResult has methods that can generate a <template> element which replaces the "holes" with {{}}, which allows storing the insertion points in the resulting DOM.

The interesting part here is that this is very compatible with the Template Instantiation proposal from Apple, which Google is a supporter of. Actually they are creating a so-called "prollyfill" for it and making a few changes from their experience with building and using lit-html.

Rendering is also quite simple, you just call render() giving it a TemplateResult and a container, such as document.body or any document fragment.

This is awesome sauce!

When you render, lit-html creates a <template> element using TemplateResult, and depending on where the insertion point is, different kinds of Pars are created, such as AttributePart, NodePart and even TemplatePart, which are then all stored in a Template object.

This is then rendered to the DOM, which means DOM is created from the <template> and values are added in the insertion points.

Picture explaining the above from Justin Fagnani, the author of lit-html

As the strings from the tagged string template literals are the same for the same template literal, it means that the Template object can easy be cached, so that when you re-render, it will not throw the DOM away and re-add it, like our troubled friend "innerHTML". Instead it will just iterate through all Parts objects and tell them to update the content at their insertion points.

We modify the contents of a <span> element and notice that Chrome only repaints that part of the DOM

Above there is a video showing this — it actually only shows repaints, but trust me, only the span is modified — which you can check via DevTools.

Knowing the holes and having access to update them via Part objects, is quite efficient, both memory wise and performance wise.

The clever reader probably noticed that you might end up updating parts that didn't change. That is true, but in many cases the DOM is often static with few places that change, so that is seldom a problem.

lit-html also has some dirty checking of values to avoid this. This even works for attributes which can be built up of multiple "holes", eg. html`<div foo=”1${‘bar’}2${‘baz’}3"></div>`

lit-html dirty checks primitive values

This is interesting when you think about custom elements base classes. Base classes, like my own experimental lit-html-element uses lit-html to render into a contained Shadow DOM. So even though these elements are not Parts, they can re-render (even using lit-html) without invalidating the DOM lit-html controls, as well as lit-html can re-render without destroying these elements state.

Data can be passed to the elements using attributes, or properties which is something the lit-html extended libraries supports. As long as these attributes and properties don't change and are primitive values, the element will not be asked to invalidate itself and re-render. As this doesn't happen with objects which can be used as properties, the element needs to avoid re-rendering unnecessarily. My base class, has a renderCallback method which can be reimplemented to implement such custom dirty checks.

Rendering a sequence of data

As JSX, lit-html allows to render sequences of data, by mixing DOM and JavaScript expressions

Rendering a list

As lit-html doesn't use a virtual DOM, appending one item to items above, basically means re-running the map method, re-creating most of the DOM — and should thus mostly be used for static content.

For this reason you can use a special directive function called 'repeat', which implements a pretty rudimentary diffing algorithm, and thus tries to re-use DOM elements by modifying them or moving them around.

I re-implemented a version myself based on the Levenshtein algorithm: https://github.com/Polymer/lit-html/pull/260

Directives

Directives are super cool, especially because it is very simple to implement your own, like the 'until' directive below:

The until directive

The until directive is quite useful, because it allows you to show some fallback content until a promise resolves. Making something that starts showing a spinner when something doesn't resolve before a certain time, should be quite simple.

until allows you to show content until a promise resolves

Other cool direction exist like 'asyncReplace', which will soon allow you to stream fetch() directly into a template. For this to work we just need ReadableStreams to become async interables which is happening.

Rendering happens when?

With lit-html, rendering happens when you call render(), but with my base class, it happens in microtasks. Ie. when a property changes, I schedule a microtask. Changing this to schedule an requestAnimationFrame or requestIdleCallback is quite easy.

To learn about when the above are called, I recommend you to checkout this talk by Jake Archibald from JSConf.asia: https://www.youtube.com/watch?v=cCOL7MC4Pl0

Like what you read? Give Kenneth Christiansen a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.