DEV Community

Cover image for Let's Build Web Components! Part 5: LitElement

Let's Build Web Components! Part 5: LitElement

Benny Powers ๐Ÿ‡ฎ๐Ÿ‡ฑ๐Ÿ‡จ๐Ÿ‡ฆ on October 22, 2018

Component-based UI is all the rage these days. Did you know that the web has its own native component module that doesn't require the use of any li...
Collapse
ย 
jwp profile image
JWP โ€ข โ€ข Edited

Benny; thank you so much for these excellent articles!

I do have a question for you. Now that you've been into litHtml and litElements for a few years now, do you still feel this is the best way to go with respect to web components?

It all looks very good to me, but I don't see widespread adoption. I don't really care what the adoption rates look like if the framework is hands down the best. I'm willing to put time into it if it is the best one.

Any advice?

Collapse
ย 
bennypowers profile image
Benny Powers ๐Ÿ‡ฎ๐Ÿ‡ฑ๐Ÿ‡จ๐Ÿ‡ฆ โ€ข

Hi John, thanks for the kind words

Yes, I do consider LitElement to be the best option for making web components in September 2020, for these reasons:

  1. it's 'close to the platform'.
  2. it's relatively simple and easy to understand
  3. it's widely adopted
  4. the community

Platform

Of all the major ways to make a web component that I'm aware of, LitElement strikes the best balance between providing features the web platform doesn't (reactive updates, data->ui templating), while still respecting and leaning on existing APIs. A LitElement factors, reads, and behaves exactly what it is - a spicy HTMLElement.

That also means that more of the knowledge you gain from working with LitElement is transferable. I've often had the experience working with developers familiar with a certain popular web framework of them asking me about "how to do X with lit", only to realize after considering their question that they really wanted to know how to do X with the DOM, or CSS, or JavaScript.

To be certain, LitElement has some opinions of its own, notably asynchronous rendering and updateComplete (which is actually kind of a super power), and you'll have fun getting to know how to take advantage of them, but most of the action here is web standards.

Simplicity

To paraphrase @webpadawan - when Vaadin was evaluating LitElement for adoption, one of the selling points for them was that it was simple enough to grok on its own that if they ever needed to, they could fork and maintain their own version without too much trouble. lit-html and LitElement have relatively few moving parts. With some dedication and a pot of coffee, you could probably get through the codebase in an afternoon. The github.com/Polymer/lit-element has about ~1700 lines of idiomatic TypeScript. github.com/Polymer/lit-html has about 3471 (2080 if you don't count the directives). And the next major versions will be smaller and faster, with more stuff opt-in.

Adoption

I'm unfortunately still aware that a certain type of personality in the industry would prefer we all think that no one's using web components, but they're gaining adoption among small and large companies. Now, npm download stats don't tell us as much as we think they do, but nonetheless, lit-element is gaining on its peers

Community

Come join us in the Polymer Slack (I know, slack... what can we do). It's a welcoming, helpful and passionate community.

TL;DR

So yeah, LitElement is where it's at, and I hope you have a tonne of fun learning and using it.

Collapse
ย 
jwp profile image
JWP โ€ข โ€ข Edited

Just curious Benny; how long have you been working with it?

It just seems almost too good to be true, but then again it's coming from the strong folks at Google, who have more than proved themselves with Angular and other things like the V8 Engine and Node.js.

I liked React in the beginning but not so much now. I think it's highly opinionated.

Thread Thread
ย 
bennypowers profile image
Benny Powers ๐Ÿ‡ฎ๐Ÿ‡ฑ๐Ÿ‡จ๐Ÿ‡ฆ โ€ข โ€ข Edited

I've been using web components since polymer 1. You can think of LitElement like polymer 4.

I liked React in the beginning but not so much now. I think it's highly opinionated

With LitElement and a nice modern toolchain your going to get a very similar developer experience, but with less overhead.

Check out modern-web.dev and open-wc.org for more

Collapse
ย 
jimisdrpc profile image
jimisdrpc โ€ข

Congratullations! Excellent article! You wrote "Unlike Polymer elements, with their two-way-binding templates, lit elements are particularly well suited to the types of one-way data flows popularized by the React/Redux pattern and others". Do you mean that lit elements isn't well suited for a webcompnent where the user enters data? You mentioned stock. I can imagine easily a webcomponent exposing stock status as an one-way (only from server to front). And if you was webcomponent for a shopping (from front to server)? Wouldn't you recommend lit-html? If so, what do you recomend if I want to create a webcomponent to be part of my corporarte webcomponet library and used for user inputs?

Collapse
ย 
bennypowers profile image
Benny Powers ๐Ÿ‡ฎ๐Ÿ‡ฑ๐Ÿ‡จ๐Ÿ‡ฆ โ€ข

Do you mean that lit elements isn't well suited for a webcompnent where the user enters data?

Just the opposite. Because of the one-way data flow, lit-html encourages you to explicitly and declaratively handle user input.

Polymer-Style Two-Way

<my-input value="{{theValue}}"></my-input>

At any given moment, what does theValue represent? Is it the state held by the parent and passing down to the child? Is it the new input being sent up to the parent by <my-input>?

One-Way Binding with lit-html

html`<my-input .value="${theValue}" @input="${onInput}"></my-input>`

Here, the developer knows exactly what's happenning. theValue is only ever passed down to <my-input>. input events are handled by onInput (which might set the state in the parent, or in a state container, or whatever you choose)

So for your company's component library, I recommend either implementing a decorator component:

<my-input>
  <input/>
</my-input>

Or implementing <input> in the root of <my-input>, and listening for the composed change event on that. Make sure to handle your state internally, e.g.

class MyInput extends LitElement {
  @property({type: String}) value = '';
  onInput({ target: { value } }) { this.value = value }
  render() {
    return html`
      <input
          .value="${this.value}
          @change="${this.onInput}"
      ></input>`
  }
}
Collapse
ย 
gkhan205 profile image
Ghazi Khan โ€ข โ€ข Edited

Hi Benny,

Thanks for this article. I have a question, I have a use case where I have to write a module which should work on React, AngularJS and Angular 2+.

So i thought to create a web component for it and use it on all other frameworks. I was wondering that how to deploy the web component code and use it in other frameworks.

It would be really helpful of you if you could help me with any example.

Thanks in advance.

Collapse
ย 
bennypowers profile image
Benny Powers ๐Ÿ‡ฎ๐Ÿ‡ฑ๐Ÿ‡จ๐Ÿ‡ฆ โ€ข

You're spot on. You should definitely publish a web component for this case.

Take a look at open-wc's recommendations on publishing

open-wc.org/guides/developing-comp...

Collapse
ย 
gkhan205 profile image
Ghazi Khan โ€ข

Thank you Benny. At the moment I'm exploring which framework or library to use, so far have explored lit and stenciljs. I'm getting attracted towards stencil.js as I can get community help for stencil more than lit.

Thank you for your reply. I will check this documentation and then will finalize which one to use. :)

Thread Thread
ย 
bennypowers profile image
Benny Powers ๐Ÿ‡ฎ๐Ÿ‡ฑ๐Ÿ‡จ๐Ÿ‡ฆ โ€ข

join the Lit and Friends Slack for community support
lit.dev/slack-invite/

Collapse
ย 
bboydflo profile image
Florin Cosmin โ€ข โ€ข Edited

this is a great article. the cons part needs to be updated. since you've written this, both lit-html and lit-element are stable

Collapse
ย 
bennypowers profile image
Benny Powers ๐Ÿ‡ฎ๐Ÿ‡ฑ๐Ÿ‡จ๐Ÿ‡ฆ โ€ข

Thanks Florin, I've updated the content

Collapse
ย 
bennypowers profile image
Benny Powers ๐Ÿ‡ฎ๐Ÿ‡ฑ๐Ÿ‡จ๐Ÿ‡ฆ โ€ข

Thanks, I'll be publishing some updates to this article shortly

Collapse
ย 
charles_lukes profile image
Son DotCom ๐Ÿฅ‘๐Ÿ’™ โ€ข

I am still confused on why I will want to convert properties to attributes and vice versa. Can you give me one use case?

Collapse
ย 
bennypowers profile image
Benny Powers ๐Ÿ‡ฎ๐Ÿ‡ฑ๐Ÿ‡จ๐Ÿ‡ฆ โ€ข โ€ข Edited

Hey, sure

So, properties exist on the DOM, which is the browser's JavaScript object representation of the Document, but they don't exist at all in the document's HTML markup.

One big difference between the two is that (for the most part), CSS can't see the DOM, it can only see the markup.

@customElement('x-l')
class XElement extends LitElement {
  @property({ type: Boolean, reflect: true })
  schwifty = false;

  render() {
    return html`
      <button @click="${this.onClick}">
        Get ${this.schwifty ? 'un' : ''}schwifty
      </button>
    `;
  }

  onClick() {
    this.schwifty = !this.schwifty;
  }
}

Here, we use reflect: true in the property descriptor for XElement#schwifty to indicate that setting the property should reflect to the attribute.

That could be useful for a document author, for example with this css:

x-l[schwifty] {
  background: url('https://media.giphy.com/media/eNpXWzGIMRjIo4lXT8/giphy.gif');
}

Another use case along these lines could be setting a disabled attribute, or setting ARIA attributes based on an element's DOM state. I set the error attribute based on a caught error's message property in <stripe-elements>, as a convenience while debugging.

You can similarly think of cases where a component author would specifically not want to leak internal state outwards to the document, like when some intermediate value is observed so that it causes re-render, while remaining a private 'implementation detail' of the element:

const handleAsText = res => res.text();

@customElement("tpl-include")
export class TplInclude extends LitElement {
  @property({ attribute: false }) content;

  _template;

  @property({ type: String })
  get template() { return this._template; }
  set template(template) {
    let currentValue = this.template;
    this._template = template;
    this.fetchContent();
    this.requestUpdate('template', currentValue);
  }

  async fetchContent() {
    this.content =
      await fetch(this.template)
        .then(handleAsText);
  }

  render() {
    return (
        !this.content ? html`<span>Loading...</span>`
      :  html`${unsafeHTML(this.content)}`
    );
  }
}
Collapse
ย 
charles_lukes profile image
Son DotCom ๐Ÿฅ‘๐Ÿ’™ โ€ข

Thank you!

Thread Thread
ย 
bennypowers profile image
Benny Powers ๐Ÿ‡ฎ๐Ÿ‡ฑ๐Ÿ‡จ๐Ÿ‡ฆ โ€ข

Small edit to the second example: set content descriptor with attribute: false, It can only be set with a property now

Collapse
ย 
gugadev profile image
gugadev โ€ข

Hi, Benny. Great post. I have a question: is there a package like React's Enzyme for Web Components to use with Jest? Or what option could I choose for TDD?

Collapse
ย 
bennypowers profile image
Benny Powers ๐Ÿ‡ฎ๐Ÿ‡ฑ๐Ÿ‡จ๐Ÿ‡ฆ โ€ข

Take a look at open-wc.org/testing

Collapse
ย 
gugadev profile image
gugadev โ€ข

Thanks Benny! I ended up with Karma and Mocha :)

Thread Thread
ย 
bennypowers profile image
Benny Powers ๐Ÿ‡ฎ๐Ÿ‡ฑ๐Ÿ‡จ๐Ÿ‡ฆ โ€ข

Great! It's what I'm using for Apollo Elements and so far so good