Cory Rylan

My name is , Google Developer Expert, Speaker, Software Developer. Building Design Systems and Web Components.

Follow @coryrylan
Lit Web Components

Build your first Web Component with Lit

Cory Rylan

- 4 minutes

This post is part two of a two-part blog series covering how to use and build Web Components. Part one covers how Web Components are used in various JavaScript frameworks as a consumer. This post, part two, will show how you can build your own Web Component with Lit.

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">&times</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">&times</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">&times</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!

View Demo Code   
Twitter Facebook LinkedIn Email
 

No spam. Short occasional updates on Web Development articles, videos, and new courses in your inbox.

Related Posts

Lit Web Components

High Performance HTML Tables with Lit and CSS Contain

Learn how to easily create HTML tables in Lit with high performance rendering using CSS contain.

Read Article
Lit Web Components

High Performance HTML Tables with Lit and Virtual Scrolling

Learn how to easily create HTML tables in Lit from dynamic data sources.

Read Article
Lit Web Components

Creating Dynamic Tables in Lit

Learn how to easily create HTML tables in Lit from dynamic data sources.

Read Article