Web Components: A Native Component Model for UI

Bernardo Cardoso
OutSystems Engineering
11 min readMay 6, 2021

In this article, we will talk a bit about web components, what they are, why they are used, who is using them, and then we will make a simple example of a web component working in OutSystems!

What Are Web Components?

Web components are a new browser feature, allowing you to create reusable custom HTML elements — with their functionality encapsulated away from the rest of your code — and utilize them across your projects.

In reality, it’s not that new, it has been around since 2013 or so. However, they recently had some major improvements and a lot more visibility, especially through Google, where they were originally developed. The overall browser support is getting very stable and the gaps are filled with working polyfills.

See full table here

To avoid confusion from the start, it can be useful to define what, technically, web components are:

  • Low Level Browser APIs.
  • Standard Component Interface.

But, even more important, is to highlight what web components aren’t:

To make it even simpler: they are more or less the native equivalent of react/angular/vue components, but independent and interchangeable between any framework used. They bring the component model approach to the browser.

When talking about web components, we are really talking about four different features:

Some of you probably already vaguely heard about the template tag or the existence of the Shadow DOM, but can’t exactly explain what they are or their purpose.

Each one of these can work separately from one another, but it’s when used together that they form what’s called a web component, and are considered part of the whole specification.

Templates: User-defined templates in HTML that aren’t rendered until called upon. The <template> tag is defined in the HTML Living Standard specification.

Custom Elements: With Custom Elements, web developers can create new HTML tags, beef up existing HTML tags, or extend the components other developers have authored. The API is the foundation of web components. It brings a web standards-based way to create reusable components using nothing more than vanilla JS/HTML/CSS. The result is less code, more modular and reusable, in our apps.

Shadow DOM: The shadow DOM is an encapsulated version of the DOM. Authors can effectively isolate DOM fragments from one another and, together with Custom Elements, make a component with self-contained HTML, CSS, and JavaScript.

HTML Modules: This feature has a more rough development, and it’s still the least stable from the web components. After some deprecations, the current feature is an extension of the ES6 Script Modules system to include HTML Modules. These will allow web developers to package and access declarative content from the script in a way that allows for good componentization and reusability and integrates well into the existing ES6 Modules infrastructure.

I won’t go into more detail about each one here, but you can check these sources for further information:

Why Use Web Components?

The first thing I want to make clear is this: maybe you shouldn’t use web components. It’s just a new option really, with positive and negative points, that you should analyze if it fits your needs, as with any other technology.

That being said, to give you a hand on some of the advantages, here’s a list with the main highlights:

  • Tech-agnostic instead of tech-specific
    This is particularly great if you’re building components on top of a design system. You want it to be as agnostic and scalable as possible. Now, with web components, you can have a core library of patterns at the design system level, without compromising reusability and technology decisions.
  • Future proofing with web standards
    Take this as you will, there’s no real “future proof” in technology in general. However, web standards should be more future proof than any given JavaScript framework.
  • Any framework or no framework
    Web components can be used with any framework or without a framework, your choice. There are, of course, some tweaks needed to make them work well encapsulated on a react component, as an example, but they do work.
  • Full encapsulation
    ShadowDom is really the star of the show here, especially for UI frameworks authors. It provides not only full functionality of each component, independently of the environment they are present in, but also enables full protection to any styles inside. The component’s author has the power to decide what is and what isn’t styled.
Unlimited power! (After the last movies, using Star Wars memes is still cool right?)

There are of course also limitations or at least some things that require a workaround to work. But most of them are actually misconceptions. Take a look at this great article if you have some concerns about the main web components myths.

Who Is Using Web Components?

I know what you’re thinking. If web components are so great, why isn’t everyone using them? Are they production-ready?

To the last question, yes! Web components are absolutely production-ready and are as evolving as any other Web standard, which means it is never really finished, it continues to grow and adapt.

About the first question, in fact, a lot of major companies are, and you probably have been using them for a long time without knowing.

Web components originally started in Google, so as you can expect they have been one of the main players pushing them. As an example, YouTube is being rebuilt using them and Apple also released their new Music App using web components. And if you’re a developer, you should be happy to know that GitHub uses mainly web components.

If you’re interested in some ready-to-use components, some major frameworks already support them:

Web Components are such a low-level API, that some libraries started to emerge, to simplify the code and make it more approachable. Here, Ionic’s Stencil has been the most prominent. However, Google very recently launched the definitive version of their web components library, called Lit! It’s a simple base class for creating fast, lightweight web components that work in any web page with any framework, providing APIs to simplify common tasks like managing properties, attributes, and rendering, bringing it closer to the React syntax.

Web Components and OutSystems

The short answer: yes, you can use web components in OutSystems.

Going into more detail, web components just use plain HTML, CSS, and mainly JavaScript, so as long as you’re willing to go high code, you can use anything in OutSystems. The real question is, should you?

Some of the advantages of the API already are solved by the OutSystems language with Blocks. They offer some kind of encapsulation and help reduce code reusability.

That being said, you could already have a set of components built, while creating another project in a different technology, and want to simply reuse them in an OutSystems application (which in fact, is one of its main use cases — Tech-Agnostic — as we saw before).

To achieve this, you have two main ways — direct reference or importing the components into the OutSystems application.

Direct reference

Here you would want to use something like a CDN that supports module mapping (for example, JSPM) or reference it directly from your GitHub repository link (if the components are just a single .js file).

With it, you need to add those links and/or scripts on the document head. This requires some high code in OutSystems but is fairly simple. Ideally, you would want to do this on the Common’s Layout block’s OnReady event, and add the following JavaScript node:

Although possible, you should avoid this solution, as it will impact the load time of these components and the overall app, as these changes are being done already in runtime.

Importing the scripts

This solution is much better performance- and general code stability-wise; however it does mean that you need to manually import them in OutSystems.

  1. Create a new script and paste the components code there. You will get some ES6 warnings. You can use this comment — /*jshint esversion: 6 */ — on top to avoid them, but the code will run either way:
  1. Add an OnReady event on the block with a JavaScript node. This is where you will register the web components on the DOM. It’s important that you do this just here and not on the script, as it will avoid lifecycle errors as the script loads faster than the UI is rendered on the screen.

Using the component

For this, you will just need to use the HTML widget, as it allows you to use almost any HTML tag, including the ones from web components. Drag it to the screen and set its tag to the component name. Done!

Limitations

  • Web components that are expecting controls like inputs on their slots (the equivalent to OutSystems placeholders) won’t work as expected, as the platform renders a span around them in runtime. One workaround is to use the slot property in a wrapper div. This way, the web component will correctly move this element to the Shadow DOM slot.
  • Extending native HTML elements, like buttons, with is=”your-custom-button” property on the Button widget also won’t work. It should be manually created using the HTML widget.
  • In general, you should avoid components with data binding, as it will make you create some extra code to get the data between the components and the platform.

Tutorial: Creating a Card Component

To wrap things up, let’s create a very simple web component and use it on an OutSystems application.

Let’s use a card as an example. It’s not very complex, so we can stay with a simple example and one of the most important atoms in any pattern’s library. It will be more or less similar to the OutSystems UI Card, but with some extra tweaks!

First, we need to choose a name for it and make it extend the HTML. Let’s use OutSystems UI as inspiration and call it OSUICard!

class OSUICard extends HTMLElement { constructor() { super(); } }

The constructor and super are mandatory from the API. This is pretty much the boilerplate of any component.

Next, we should start by creating the necessary HTML. As this is just a card, it will be very simple.

var shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.innerHTML = `
<div class="card">
<slot>Insert Your Content</slot>
</div>
`;

As you can see on the code above, we are creating the card inside the Shadow DOM with the attachShadow method. This will make it truly encapsulated from the rest of the "normal" DOM.

Shadow DOMMMMM!
The Shadow DOMMM!!!! (Nevermind this, I always think I just summoned a D&D boss when I say Shadow DOM…)

The slot tag that you see is one of the most exciting features, as it allows you to define placeholder elements, where HTML can be entered by the developer. It’s more or less equivalent to the OutSystems blocks placeholders. We can also set a default value. In this case, I’m adding just the “Insert Your Content” text, but we could use HTML here that will only be replaced if the developer uses the placeholder.

In fact, we don’t really need a slot here, any elements inserted would already be on the right spot, as this is a single div component. However, you can imagine how useful this is if you have a more complex HTML, and want just a particular element to be replaced.

Now that we have the structure, comes the styles! As we mentioned before, Shadow DOM is great for authors, as outside styles don’t bleed in the HTML inside, not even !important (yesss!). The only thing that can go through are CSS custom properties (CSS Variables), which is great for keeping consistency and scalability in a design system.

The trick is, the component must be built preparing for that. This should be adapted to your needs really. Here, I tried to make sure things were prepared to be impactful for demo purposes, maybe you don’t generally need as many variables for just a card, but keep in mind that the main frameworks normally have around 20 variables for each component. Of course, the more variables you create, the more open to customization it will be.

As an architectural good practice, I prefer to set a variable to everything, but give it a fallback value (yes, you can do that with CSS variables). So, here’s the styles that we will append on the Shadow DOM:

<style>
:host {
display: inline-block;
width: var(--card-wrapper-width, 100%);;
}
.card {
align-items: var(--card-align-items, center);
background: var(--card-background, #fff);
border-radius: var(--card-border-radius, 16px);
border: var(--card-border-size, 1px) var(--card-border-style, solid) var(--card-border-color, #dee2e6);
box-shadow: var(--card-shadow, 0 6px 8px rgba(0, 0, 0, .1));
box-sizing: border-box;
color: var(--card-color, #000);
display: var(--card-display, flex);
flex-direction: var(--card-flex-direction, row);
height: var(--card-height, 100%);
justify-content: var(--card-justify-content, space-between);
padding: var(--card-padding, 16px);
text-align: var(--card-text-align, left);
}
</style>

The :host selector that you can see at the start refers to the component itself (in this case, the OUSICard). It's more or less like the 'this' from JavaScript.

With this, the web component is pretty much done and ready to be used anywhere you want. The only thing missing is to register it. We could have already done that, but as we want to use it in OutSystems, we should just do that on the OnReady of the layout.

After following the process described in the previous chapter, we can check the final result here.

Card demo

What’s Next?

Web Standards Strikes Back!
Web Standards Strikes Back!

This might have come a bit later than we wanted, and some will probably say too late, but I believe with time, developers will eventually return to the native solution, as the standard develops and the gap starts to feel very close. CSS and SASS have a similar story, with the first one slowly but firmly catching up on the relevant features.

As you can see, there’s a lot of potential on all of these, and now is the time to start experimenting with it! It’s the opportunity to make our patterns native, more performant, more accessible and independent of whatever technology you want to wrap around them. If you think this fills the needs of your project, make the leap!

References

Originally published at https://outsystems.com.

--

--

Bernardo Cardoso
OutSystems Engineering

Is an avid lifelong learner, and front-end developer at OutSystems who loves the collaborative, and problem-solving nature of his job.