Welcome to Lit University
Howdy everybody. Welcome To Lit University Basics. I'm Elliot from the Lit team at Google, and today's lesson: Lit.
So we're in episode 3 of Lit University and we're finally gonna go over what is Lit. And by the end of this video, you will understand what Lit is, what features it provides, and maybe just why I think it's so awesome. Come along.
What Are Web Components and Lit?
Web components are the browser's built-in component model, composed of Custom Elements, which provide a way for you to create your own HTML elements with a JavaScript API that seamlessly integrate with the browser, and Shadow DOM, which provides a way to have encapsulated DOM and styling.
What is Lit? Lit is a library that makes it easy to build simple, fast, interoperable web components. Lit provides a very minimal layer on top of a already low-level web component API to provide an excellent developer and user experience.
How does Lit provide a great developer experience? Well, let's jump over to lit.dev. Lit packages your components in reusable, interoperable custom elements. It scopes your styles and component contents in Shadow DOM. It helps you model your component's API and efficiently re-render with reactive properties. And it enables you to write readable, expressive expressions using declarative templates, all without a build step.
With Lit, you can build anything from shareable components and design systems that work across frameworks. Lit components are strongly encapsulated to the point that your users don't even need to know that your custom element was even built with Lit. It's an implementation detail. You can then use these components to build entire applications.
The Core Benefits of Lit
To recap, Lit simplifies creating web components. It's also really fast, and it does it in only five kilobytes. And finally, Lit components are interoperable, since Lit's features are just browser features.
Pretty cool, right? Now, let's dive deeper on each of these features, starting with custom elements.
Defining a Custom Element
Let's jump into the Lit playground and scratch up a quick little element. There will be links to all the finished sections in the description.
This playground has a pretty empty index.html. The lit.dev playground doesn't do anything special with compilation of HTML files. This is just as if you were to create an index.html on your local machine.
We also have a say-hello.ts TypeScript file with some imports already filled out for us. Lit can be written with JavaScript and zero compilation needed, but for this video, we're going to use TypeScript because I like types and JavaScript decorators are not in the browser as of the making of this video. So if you're from the future, first of all, cool, but also make sure that you check out our documentation on lit.dev because the decorator semantics may have changed a tiny bit. If you'd like to learn more about how to build with JavaScript, check out lit.dev and switch the code slider to JavaScript.
A Lit element starts off with a class that extends LitElement. Let's create a class that is called SayHello and make it extend LitElement.
So export class SayHello extends LitElement. And each Lit element is a native HTML custom element. In Lit, you can tell the browser to associate a tag name with this class by using the @customElement decorator. So let's tell the browser that our custom element should have the say-hello tag name. @customElement('say-hello').
And let's render "hello". So in the render function, return 'hello'. Now that we have defined the element, let's import the custom element definition by loading the say-hello file in index.html. And now let's use the element.
<script type="module" src="./say-hello.js"></script> the compiled output of say-hello.ts.
Nice, hello.
The browser knows that the logic of our SayHello class is associated with the say-hello custom element tag name. The browser only needs to know the tag name to associate our logic with an element. That means that this custom element will work wherever you can write HTML elements with random tag names, like in Markdown, Angular, or even React.
Declarative HTML Templates
So we can associate some logic with our component, but let's actually render some DOM to the page using Lit's declarative templates. Let's jump back into the code.
We can render DOM inside of our Lit element by defining a render method and returning a Lit template. So, render(), and we'll just say return, and then a Lit template that is defined like so: html backtick, and then we'll insert some DOM right here.
You might notice the syntax of html and a backtick string. The backtick string is actually a native JavaScript syntax called a template literal string. And html is a function that lets us supercharge how we're handling the string. When a function is invoked like this, it is called a tagged template literal.
Tagged template literals run natively in JavaScript and are the main reason we call the library Lit. The name Lit also comes from the library being incredibly little, and also being just plain lit. But some cool things about Lit templates is that they are really, and I mean really, fast. They're also very expressive. You can fit JavaScript inside of them using template literal interpolations because expressions in Lit templates are just JavaScript.
Dynamic Templates and Event Handling
Let's jump back into the code and let's interpolate some data into the template. Let's insert a name into the template. So const name = 'world', and then in the paragraph tag, ${name}.
You can add event listeners with the @ syntax. For example, let's log something to the console when we click that name. So main will do the @click equals, then we pass it a function, console.log('clicked').
Lit templates are mostly just HTML with expressions and a couple of special characters for special operations. For example, you can also set attributes, toggle attributes, set properties, conditionally render, or even render lists of items. This doesn't even scratch the surface of what Lit templates are capable of. So we'll go deeper into this in a later video, but you can read more on this stuff right now on lit.dev. Link to the docs and tutorials down in the description. But for now, let's just render "Hello, my name is ${name}".
Nice.
Scoped Styles with Shadow DOM
Another great feature in Lit is scoped styles. And the best part? Lit tries to do as little magic as possible. It's just using fast, browser-native Shadow DOM. Let's jump back into the code.
You can add styles to your Lit element by setting the static styles getter on the class and using the css tag template literal. So, static styles = css backtick backtick. We could then write CSS as if we were just writing in a style tag, because Lit is basically just doing this under the hood.
Here I have some styles I've been saving just for this moment. Let's walk through them.
First, you may notice that we have very simple selectors like header, main, h1, and the p tag. In the global scope, this would create issues because the selectors are so generic and tend to accidentally select elements that you don't expect. For example, notice that the header element has color: white.
Let's place a header in our index.html. So header, this is outside of our element. It's unstyled. Now let's try to style the header elements from index.html. So create style tag, header { color: red; }. We'll even add an !important for good measure to make sure that the selectors are not overwriting it.
It doesn't style the header inside the element. Why is this? Let's take a look and open the dev tools inspector. Notice that all of our component's DOM is inside of a thing called a shadow root. Shadow roots are a native browser feature that Lit uses to scope styles and DOM, preventing styles from leaking in and out of the component.
Shadow DOM is a very big and juicy topic, so we'll go over that in depth in a later video. But if you want some shadowy content right now, my super amazing teammate Andrew's latest Build it with Lit video on styling gives a fantastic, sugar-filled overview on styling with Shadow DOM.
Before continuing, let's do some cleanup and delete some of this header code.
State Management with Reactive Properties
So now we have a really fun name tag, but it has a name that doesn't really change, and that's no fun. Let's make this name tag update when you provide it a new name.
Lit makes this possible by providing something called reactive properties. Reactive properties model your component's API and internal state and will trigger the update lifecycle and re-render the template every time these properties are changed. The Lit update lifecycle is a series of callbacks that Lit calls to perform async, batched renders to provide you with top-notch performance. Lit also provides your component with lifecycle callback methods on top of the native custom element callbacks to hook into. We'll do another video on the Lit update lifecycle at a later date, but for now, let's just focus on reactive properties. Let's try them out.
We want the name to be configurable, so let's define the name public reactive property using the @property TypeScript decorator. So, @property() name = 'world'; and let's use it in the template: ${this.name}.
Reactive properties are cool. There are public reactive properties and internal state reactive properties, and we just declared the name property as a public one. When changed, all reactive properties will cause the element to trigger the update lifecycle, which is the primary way to trigger re-renders in Lit. Additionally, Lit will expose each public reactive property as its public API as both an attribute and a property to the users of your component.
Interacting with Components and Interoperability
Now that we've exposed the name public reactive property, let's go set it. Let's open up index.html and enter your name. <say-hello>, and here we will put name="Elliot at @techytargets".
As I said, reactive properties will cause your component to re-render whenever they're changed. So let's go into dev tools and change the name property programmatically. $0.name = 'Lit University Student'. Nice. The element reacts to the changes in the property. Reactive properties can get even spicier with special lifecycle callbacks. If you want to learn more, I've added a link to a tutorial in the description.
Now we have a fully featured Lit element. If we wanted to, we could copy-paste it several times and reuse it. And since Lit makes it super easy to build with browser features like custom elements, this component should work wherever HTML works, including other web frameworks. Watch the ending of Andrew's first Build it with Lit video to see this in action.
Conclusion & Further Learning
And because Lit is incredibly simple, fast, lightweight, and interoperable, many developers out there are building with Lit to create shareable components, design systems, and fully-fledged applications. And I hope that you will too.
That wraps up today's lesson on Intro to Lit. I've left plenty of extra credit reading in the description, and links to some Lit tutorials and videos that I think are quite relevant to take you further from here. If you have any questions, tweet at us with the hashtag #AskLitDev. Also join our Lit and Friends Slack where there are thousands of fantastic people chatting Lit and web components.
Class dismissed.