Build your first Web Component with Lit
Cory Rylan
- 4 minutes
In our previous part one post, we covered how to consume and use a Web Component in an application. In this post, we will see how to build the alert Web Component using Lit.
The library Lit provides a lightweight set of tools, making it simple to create fast and reusable Web Components. Lit provides a lightweight base class and templating syntax that makes Web Components easy to build while keeping a light footprint on bundle size.
Lit provides a base class and templating syntax to reduce the boilerplate of native Web Components. It also offers several standard utilities to make everyday tasks easier. In this demo, we will use TypeScript to take advantage of the decorator syntax and Lit utilities.
Creating a Custom Element
First, we need to define our alert Web Component by registering it to the Custom Element registry.
import { LitElement, html } from 'lit';
import { customElement } from 'lit/decorators.js';
@customElement('ui-alert')
export class UIAlert extends LitElement {
render() {
return html`
<div>
Hello there!
</div>
`;
}
}
Lit provides a customElement
decorator making it easy to register our Web Component. Our UIAlert
extends the LitElement
base class. To render a template, we implement a render()
method and return a tagged template literal using the html
template tag. This enables us to create templates that can be updated and re-rendered using JavaScript template strings (backtick syntax).
Content Slot
To project content into our component, rather than hard coding the template, we can use the Content Slot API.
import { LitElement, html } from 'lit';
import { customElement } from 'lit/decorators.js';
@customElement('ui-alert')
export class UIAlert extends LitElement {
render() {
return html`
<div>
<slot></slot>
</div>
`;
}
}
Now users of our alert can place content between the element tags, and the content will render within the bounds of our component.
<ui-alert>Hello there!</ui-alert>
HTML Attributes and JavaScript Properties
Data can be set on a Web Component via HTML Attributes or JavaScript properties. Lit simplifies this by providing a @property
decorator that enables the value to be set via attributes or properties. When the value is set, Lit will re-render the template only where the property is referenced.
import { LitElement, html, css } from 'lit';
import { customElement, property } from 'lit/decorators.js';
@customElement('ui-alert')
export class UIAlert extends LitElement {
@property({ type: Boolean }) dismissable = false;
@property({ type: String }) status: 'info' | 'success' | 'warning' | 'danger' | '' = '';
render() {
return html`
<div>
<slot></slot>
${this.dismissable ? html`<button aria-label="close alert">×</button>` : ''}
</div>
`;
}
}
Styles
Lit provides a css
function for template styles. The styles automatically use Shadow DOM and will only apply to our component template.
import { LitElement, html, css } from 'lit';
import { customElement, property } from 'lit/decorators.js';
@customElement('ui-alert')
export class UIAlert extends LitElement {
@property({ type: Boolean }) dismissable = false;
@property({ type: String }) status: 'info' | 'success' | 'warning' | 'danger' | '' = '';
static get styles() {
return [css`
:host {
--background: #eaeaea;
--color: #2d2d2d;
}
div {
background: var(--background);
color: var(--color);
display: flex;
align-items: center;
padding: 12px;
line-height: 1em;
font-size: 16px;
position: relative;
min-height: 24px;
border-radius: 4px;
font-family: Helvetica, Arial, "Lucida Grande", sans-serif;
font-weight: 300;
}
:host, slot {
display: content;
}
:host([status='success']) {
--background: #bae0ba;
}
:host([status='warning']) {
--background: #fff2c2;
}
:host([status='danger']) {
--background: #ffbebe;
}
:host([status='info']) {
--background: #aad7ff;
}
button {
margin-left: auto;
color: var(--color);
background: rgba(0, 0, 0, 0.1);
border: 0;
font-size: 20px;
width: 24px;
height: 24px;
cursor: pointer;
line-height: 0;
display: none;
}
button:hover {
background: rgba(0, 0, 0, 0.15);
}
:host([dismissable]) button {
display: block;
}
`];
}
render() {
return html`
<div>
<slot></slot>
${this.dismissable ? html`<button aria-label="close alert" part="close-button">×</button>` : ''}
</div>
`;
}
}
Using CSS Custom Properties and the CSS Parts API we can enable users to change specific CSS properties on our component or entire element nodes.
Custom Events
Lastly, we can emit Custom Events with Lit just like any standard Web Component.
import { LitElement, html, css } from 'lit';
import { customElement, property } from 'lit/decorators.js';
@customElement('ui-alert')
export class UIAlert extends LitElement {
@property({ type: Boolean }) dismissable = false;
@property({ type: String }) status: 'info' | 'success' | 'warning' | 'danger' | '' = '';
static get styles() {
return [css`...`];
}
render() {
return html`
<div>
<slot></slot>
${this.dismissable ? html`<button @click=${this.emitCloseChange} aria-label="close alert" part="close-button">×</button>` : ''}
</div>
`;
}
private emitCloseChange() {
this.dispatchEvent(new CustomEvent('closeChange'));
}
}
Custom Events can be triggered from any user interaction or async task. Custom Elements can also pass back data via its second options parameter.
this.dispatchEvent(new CustomEvent('closeChange', { detail: 'a custom value' }));
Now we can use our alert component like any other HTML Element in our applications.
<ui-alert status="success">hello there!</ui-alert>
<script type="module">
import 'alert.js';
const alert = document.querySelector('ui-alert');
alert.dismissable = true;
alert.addEventListener('closeChange', event => {
console.log(event);
});
</script>
Lit provides an easy way to build Web Components that's lightweight and gives us reactive properties and templates. Check out the full working alert demo component below!