Lit is one of the most interesting front-end JavaScript frameworks for reactive programming. It has garnered a lot of interest from developers, but remains relatively under the radar compared to other responsive frameworks. Lit is built on the web components standard and prioritizes speed and a small set of useful features.
Create web components with Lit
There are several ways to start a Lit project. For this tutorial, we’ll use a starter template. You will need a command line with Git and Node.js (npm
) installed.
To note: Because it is based on Web Components, Lit uses the Shadow DOM to gain in reactivity.
Go to the command line and type git clone https://github.com/lit/lit-element-starter-js.git
. This command deploys a simple sample project into the lit-element-starter-js
phone book. cd
in this directory and install the dependencies with npm install
.
Now you can run the application with npm run serve
. If you visit localhost:8000
you will see the application running with the screen shown in Figure 1. (Click the button To count button increases the number.)
Figure 1. The Lit startup application
Responsiveness in the Bed frame
Next, let’s look at how Lit is laid out and how it achieves responsiveness.
The main work takes place in /dev/index.html
as shown in List 1.
Listing 1. Reactivity in Bed
Demo This is child content
The main thing to note is that the page imports a Polyfill library to ensure that the browser will understand the web components. It also imports a library to make it easier to load web components via npm
. Finally, it imports a custom module, my-element.js
from the parent directory.
Then take a look at my-element.js
in Listing 2, which contains all the fundamental elements of a lit
making up.
Listing 2. Elements of a Lit-based component
import {LitElement, html, css} from 'lit'; /** * An example element. * * @fires count-changed - Indicates when the count changes * @slot - This element has a slot * @csspart button - The button */ export class MyElement extends LitElement { static get styles() { return css` :host { display: block; border: solid 1px gray; padding: 16px; max-width: 800px; } `; } static get properties() { return { /** * The name to say "Hello" to. * @type {string} */ name: {type: String}, /** * The number of times the button has been clicked. * @type {number} */ count: {type: Number}, }; } constructor() { super(); this.name="World"; this.count = 0; } render() { return html`${this.sayHello(this.name)}!
`; } _onClick() { this.count++; this.dispatchEvent(new CustomEvent('count-changed')); } /** * Formats a greeting * @param name {string} The name to say "Hello" to * @returns {string} A greeting directed at `name` */ sayHello(name) { return `Hello, ${name}`; } } window.customElements.define('my-element', MyElement);
The first thing to note is that lit
extend the LitElement
base class. You might also observe that the styles()
and properties()
are both static getter methods. We don’t have to worry about signatures; we just use them to define the characteristics of our components.
the styles()
The method returns the CSS for the component, and properties()
returns the properties. styles()
use it css
Lit method to define component-scoped CSS in a template literal. properties()
exposes the reactive variables of the component: name
and count
. In each case, the returned object defines the type of variable in the property (for example, name: {type: “String”}
).
Property values
Note that properties are given default values in the constructor. These are public properties, so you can comment out the definition of the name, for example, then in /dev/index.html
assign a value to the name property through its parent, as shown in Listing 3.
List 3. Value of public property
This is child content
The List 3 pattern is common among reactive frameworks, allowing unidirectional, downward state flow from parent to child.
Component rendering
The next step is the render
method, which returns the markup for the view via the html()
method and a template literal. The syntax used in the string literal is tagged literal. Note that this method has full access to the two properties we saw earlier (name
and count
), as well as the methods defined on the component object, which are defined next.
Built-in and custom methods
Lit has two types of methods: built-in and custom. the _onClick()
The method is built-in, which means it has special meaning for the framework. When Lit sees this method, it sets the onclick handler for the given component. In this case, it simply increments the count variable.
The second method is sayHello(name)
, which is a custom method called from view markup. Note that this method has full access to variables that can be passed as arguments:
${this.sayHello(this.name)}!
All properties and methods are accessible through the this
keyword; that is, they are members of the object.
Component registration
Finally, the component asks the browser to register itself as a web component.
As with other reactive frameworks, Lit encourages you to write one-way code, where the model simply reflects state changes without directly modifying the state or the DOM.
Locations and child elements
The Web Components standard uses slits. In our example, you can see that the component itself defines a This is child content
and the parent passing in a child element (
) that will be placed in the slot.
Show and hide items
Lit includes a variety of useful expressions, such as the ability to show and hide items.
For example, if you wanted to display an item when the counter exceeded 5, you could use something like what is shown in Listing 4.
Listing 4. Showing and Hiding Elements
return html`${this.sayHello(this.name)}!
Not hidden`;
Added functionality in Lit
Now let’s add some functionality to the example. How about displaying collections? Take a look at List 5.
Listing 5. Iterating over an array in Lit
static get properties() { return { name: {type: String}, count: {type: Number}, hobbits: [] }; } constructor() { super(); this.hobbits = ["Frodo","Sam","Merry","Pippin"]; } render() { return html`${this.sayHello(this.name)}!
Not hidden
-
${this.hobbits.map((color) =>
html`
- ${color} ` )}
Listing 5 shows how to add a hobbit
property, initialize it with the four most famous hobbits, then cycle through them using a map in the render function. You might notice that React handles this sequence very similarly. You can also extract your looping logic into a function that you call from inside the template, as described here.
To note:Lit also provides a repeat directive, which you can use to efficiently handle list state changes in certain situations.
Make Lit API calls with up to
Now let’s see how to make a remote API call. We will use the Lord of the Rings API to get a list of all known hobbits. You’ll also need to retrieve an authentication token, which is free and quick.
Bed has a until
which allows you to display alternative content while waiting for the resolution of a promise. First, add this import to the head of your my-element.js
: import {until} from 'lit/directives/until.js';
.
Then add the remoteHobbits
to the manufacturer: this.remoteHobbits = getRemoteHobbits();
.
Third, add the output to the render method like this:
${until(this.remoteHobbits, html`Awaiting remote hobbits...`)}
Notice that here we use until
to display a loading message while waiting for the resolution of the promise of remoteHobbits
.
Take a look at the definition of promise in getRemoteHobbits()
shown in Listing 6.
Listing 6. getRemoteHobbits
const getRemoteHobbits = async () => { const response = await fetch("https://the-one-api.dev/v2/character?race=Hobbit", { "headers": { "Authorization":"Bearer" } } ); const json = await response.json(); const hobbits = []; for (const h of json.docs){ hobbits.push(html` ${(h.name)} `); } return hobbits; }
Listing 6 uses a normal fetch call to get the list of characters, filtered by race=Hobbit
. Note that you must provide your API token in the Authorization
on your mind.
Finally, you gather the JSON response into a set of list items and send it back to insert into the HTML template.
Now the UI will show the remote API hobbits collection.
You can find the source for this demo in my GitHub repository.
Copyright © 2022 IDG Communications, Inc.