DEV Community

Manuel Martín for Open Web Components

Posted on

The evolution of Open-wc scoped-elements

In this blog post I'm going to talk about the evolution of @open-wc/scoped-elements package. If you want to know the context behind it and why we created it, you might want to read my previous blog post first.

A few months ago we shipped the first experiment about scoping web component definitions, @open-wc/scoped-elements, allowing us to use in our apps different versions of the same components. Since then, we learned and improved some things about using scoped elements that I would like to share with you in this post.

First, we improved our development experience. From using the createScopedHtml function we have gone to using a Mixin for our LitElement components, so now the use of scoped-elements looks like this:

import { LitElement, html, css } from 'lit-element';
import { ScopedElementsMixin } from '@open-wc/scoped-elements';
import { FeatureA } from 'feature-a';
import { FeatureB } from 'feature-b';

export class PageA extends ScopedElementsMixin(LitElement) {
  static get scopedElements() {
    return {
      'feature-a': FeatureA,
      'feature-b': FeatureB,
    }
  }

  static get styles() {
    return css`
      :host {
        display: block;
        padding: 10px;
        border: 2px solid #ccc;
      }
    `;
  }

  render() {
    return html`
      <h3>I am page A</h3>
      <feature-a></feature-a>
      <feature-b></feature-b>
    `;
  }
}

As developers, we can now apply the ScopedElementsMixin to our component and add the static scopedElements method, with the elements that we want to scope. So far so good!

But, what happens if we don't know which element we want to use at the moment of the scoped elements definition? It could happen, for example, that we may want to lazy-load some components. To cover this use-case the mixin has a method named defineScopedElement(tagName, element) that allows us to define scoped elements at any time. Let's see an example:

import { LitElement } from 'lit-element';
import { ScopedElementsMixin } from '@open-wc/scoped-elements';
import { MyPanel } from './MyPanel.js';

export class MyElement extends ScopedElementsMixin(LitElement) {
  static get scopedElements() {
    return {
      'my-panel': MyPanel,
    };
  }

  connectedCallback() {
    super.connectedCallback();

    import('./MyButton.js').then(({ MyButton }) =>
      this.defineScopedElement('my-button', MyButton));
  }

  render() {
    return html`
      <my-panel class="panel">
        <my-button>${this.text}</my-button>
      </my-panel>
    `;
  }
}

In the previous example, my-button is not registered as a scoped element in the static scopedElements method because it is loaded lazily, and once loaded it is defined through the definedScopedElement function, which causes the tag my-button to be upgraded to the actual component.

Last but not least, it could also happen that in an awesome feature you are implementing you need to get a scoped tag name for any other reason or perhaps you want to create the element through document.createElement 🤷‍♂️.

Static method getScopedTagName to the rescue! This method returns the scoped tag name used by your component for a specific tag name. Even if the component that uses that tag name is not yet defined! (remember, lazy components 😉).

import { LitElement } from 'lit-element';
import { ScopedElementsMixin } from '@open-wc/scoped-elements';
import { MyPanel } from './MyPanel.js';

export class MyElement extends ScopedElementsMixin(LitElement) {
  static get scopedElements() {
    return {
      'my-panel': MyPanel,
    };
  }

  constructor() {
    super();

    const scopedTagName = this.constructor.getScopedTagName('my-panel');

    // do whatever you need with the scopedTagName
  }

  // ...
}

But those are not the only improvements made by the use of the mixin. Another important point is that this type of use will probably allow us to upgrade our mixin to use scoped custom element registries behind the scenes once they are a reality, so our applications will not need to be migrated whenever this happens. Great, isn't it? 🤗

Finally, scoped-elements has been very useful in my company because we have a large shared component library that recently released a major version, and scoped-elements allows us to migrate our apps seamlessly without doing a big bang.

You can find more information about scoped-elements in Open-wc website.

Top comments (0)