Lit library series — 1: A Deep Dive into lit-html Library

Saravanan A R
WhatfixEngineeringBlog
6 min readMay 9, 2023

--

Photo by Jackson Sophat on Unsplash

The purpose of this blog series is to delve deep into the Lit library and gain a comprehensive understanding of its unique features and how it sets itself apart from other libraries and frameworks in the market.

The series is divided into three parts, covering topics such as
1. How lit-html works?
2. How Lit Element performs batch updates on the DOM?
3. A comparison and bench marking of Lit with other frameworks and libraries, along with its potential use cases.

In this particular blog, we will focus on exploring lit-html and its inner workings. But before delving into lit-html, let’s first get an overview of the Lit library itself.

What is Lit?

Lit is a lightweight web component library with a small footprint of approximately 5KB.

It is specifically designed for efficient and rapid development of UI components using web component specifications, and it further enhances performance by introducing batch updates to the DOM.

In fact, according to a public benchmark conducted by https://krausest.github.io/js-framework-benchmark/index.html, Lit Element has demonstrated faster performance compared to React.

To learn more about Lit Element, you can visit their official website at https://lit.dev/. However, the focus of this series is to understand how Lit Element works and how it is implemented.

Lit Element is built on top of lit-html, which is a template library used for creating templates, and batch DOM update logic. Lit Element extends lit-html by providing additional functionality for batch updates.

Creation of lit-html template:

The lit-html is a JavaScript template library that enables the creation of templates using JavaScript tagged literals and tag functions, leveraging native JavaScript features and web components.

Unlike JSX or other template libraries, lit-html does not require a build step to convert framework-specific templates or syntax into browser-compatible templates or syntax, as it utilizes native JavaScript features directly.

Let’s create a template using the lit-html library:

An example of lit-html component — Template A

In this example, we import two functions from the lit-html library:
1.html, which is a tag function.
2.render, which is used to render the template to the DOM.

Next, we define a function called myTemplate which takes an argument name.

The template is defined using the html tag function, enclosed in back ticks (``), and contains an HTML snippet <div>Hello, ${name}</div>.

The ${name} is a placeholder that can be dynamically replaced with a value at runtime based on the argument passed to the function myTemplate.

The html tag function returns a TemplateResult object, which represents the template that can be rendered to the DOM.

In the upcoming sections, we will explore JavaScript tag function and how the html tag function is implemented in the lit-html library.

In the final line of code, we are invoking the render method, which renders the template.
The render method accepts two arguments:
a)TemplateResult object that represents the template to be rendered.
b) Reference to the DOM element where the template will be rendered.

Tag function in JavaScript:

Note: If you’re already familiar with tag functions, you can skip this section.

Tag function in JavaScript is a special type of function that can be used to process template literals, which are strings enclosed by back ticks (``).

The tag function is invoked immediately before the template literal is evaluated, and it receives the template literal and its interpolated values as separate arguments.

For example, consider the following code:

var name = "Whatfix";
function sayHello(literalString, ...values) {
console.log(literalString); //Output: ['Hello, ', '', raw: Array(2)]
console.log(values); //Output: ['Whatfix']
}
sayHello`Hello, ${name}`;

In this example, sayHello is a tag function that takes a tagged literal Hello, ${name} as its argument.
The tag function is invoked immediately before the tagged literal is evaluated. It then breaks down the tagged literal into two parts:

a. An array of static parts of the template literal: literalString.
In our example, the value of literalString parameter is ['Hello, ', '', raw: Array(2)]

b. One or more arguments corresponding to the number of expressions in the tagged literal: values. In our example, we are using the spread operator (...) to represent it as an array. Its value is ['Whatfix'], which contains the values of the expressions used in the template literal.

A deep dive into html method:

Here is an implementation of the html tag function from the lit-html repository — https://github.com/lit/lit . (I made some improvements to the code by removing unnecessary parts to make it more understandable).

const html =
<T extends ResultType>(type: T) =>
(strings: TemplateStringsArray, ...values: unknown[]): TemplateResult<T> => {
return {
['_$litType$']: type,
strings,
values,
};
};

Similar to other tag functions, the html tag function also breaks the tagged literal into two parts and accepts them as arguments.

It then forms an object, known as TemplateResult, with three parameters and returns it.

The three parameters of TemplateResult includes _$litType$, strings, and values[].

a. The _$litType$ parameter contains the type.
b. The strings parameter holds an array of the static parts of the literals.
c. The values[] parameter contains the values of the dynamic parts of the literal.

In our previous example(Template — A), myTemplate("whatfix”)method will return:

{
_$litType$: 1,
strings: ["<div>Hello, ", "</div>", raw: Array(2)],
values: ["whatfix"]
}

This entity is referred to as a TemplateResult.

A deep dive into render method:

The render method in lit-html is responsible for rendering the component in the UI.
It accepts three arguments:

a. value — A TemplateResult that is generated by the html tag function, as discussed in the previous section.

b. container — A reference to the DOM element where the template will be rendered.

c. options — For the purpose of simplification, this argument can be ignored to better understand the process.

We can visually understand the workings of the render method in our example by utilizing a flow chart.

High level working model of render method in lit-html

Next, let’s process our example component, Template A, using the aforementioned flow chart. Since we have already generated a TemplateResult object using the html tag function, as discussed in the previous section, we can now proceed with the rendering process.

The TemplateResult object for our example component, Template A, will take on the following structure:

TemplateResult object for Template — A component

The subsequent step involves generating the <template> element from the TemplateResult object.
In our example component, Template A, which includes a dynamic part represented by {name}, we are generating a random nine-digit number and add it as a comment node in place of {name}.
As a result, the <template> element will appear as follows:

<template> element for Template-A component

The subsequent step involves cloning the template element and updating the dynamic parts, either during the first-time rendering or subsequent updates. The cloning the template element happens only in initial render of the component.
The resulting structure will appear as follows:

The final structure of Template-A component

Then using the second argument passed to the render method, we are adding the final component structure in the Document.

Conclusion:
In this blog, we’ve explored the internal workings of lit-html. In the next part of this series, we’ll delve deeper into the batching operations performed in the lit library with lit elements.

--

--