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
John Peters • 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
John Peters • 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
 
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
 
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
 
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