If you have been working on UI for a bit, you may have come across willing to reuse UI parts. Here we will cover the different approaches for sharing UI bits between projects.
- "Bootstrap" approach
- Javascript API approach
- "Share button" approach
1. Bootstrap Approach
You basically take a bunch of CSS and Javascript from the documentation, copy paste all of the script tags to your project, because figuring out which you exactly need is time consuming. Then you go back to the documentation, you copy paste your favorite component's HTML markup.
Cons: once you have copy pasted, you don't exactly know what parts of the markup you can change without breaking the UI. When you look at the attributes you don't know which are mandatory, which are semantic, which are mandatory, which are accessibility related etc. So you spend a lot of time reading the whole documentation.
2. Javascript API / SDK Approach
In this approach there is some Global Javascript (let's say Google API), that we import. Then, once the script has been fetched, it will call a our Global function. We also create a dom element that will act as a container, pass it to the SDK which will magically place its content in it.
Pros: There is some abstraction, so we don't have to mingle with the details. Cons: It requires DOM access, which is something that modern libraries like Angular or React discourage you to do, because it will be very difficult to server side render. The other thing is Global functions, which we should avoid.
3. "Share button" approach
Share button approach from Facebook. It's a mixture of loading some global javascript.
Cons: This is really confusing because they do some asynchronous javascript loading without using the async
and defer
attribute, instead they inject the script manually. Then once that happens, you have to place somewhere on your page some HTML that has to have a specific shape, by having a class and a bunch of data attributes that will be wrapped by the SDK and magically inject in the button.
Goodish: The cool part is that if the SDK hasn't loaded, then we can simply put an anchor tag and it will simply work, but at the same time we see that at that point we have duplicate code with the url that we need to keep in sync, and it is very easy to overlook this.
Poor Developer Experience
If you write vanilla javascript, these approaches are ok, but once you try to couple them with libraries like React, Angular etc. then it quickly degrades to a poor developer experience, because
- we access the DOM in a lot of cases (which they actively discourage).
- It also requires you copy paste a bunch of code, so we quickly end up writing wrapper components around these things.
These wrapper components end up being out of sync, different features are implemented in one library not the other etc: one is trying to catch up with a security patch, the other is maintained by someone from the community who does not care about it anymore because they moved to another project etc. These create poor DX. For example for MDL (Material Design equivalent of Bootstrap) which has some stylesheets, some global javascript, and people write wrapper components around this; there is one for React, Preact etc.
Web Components
Is this native thing that has been defined in 4 specs
- Custom Elements
- HTML Templates
- Shadow DOM
- HTML imports
The great thing is that you can use them as a div.
Attributes vs. Properties
The difference between attributes and properties is really important.
Element Attributes are those that you define in html markup. They allow you to pass information to your component, but they only support simple data like string. We could stringify data and pass it in but performance would be really bad.
<date-picker value="5" disabled></date-picker>
Element Properties are the properties on an instance of a class. But the two are not directly connected, even though we could connect them by implementing getters and setters on the element class and use this.setAttribute()
, this.removeAttribute()
or this.hasAttribute()
.
Anyways, here we are not concerned about this. Just for the sake of demonstration, properties are for example :
const el = document.querySelector('date-picker');
el.value = 99;
But unlike with attributes, we can pass more complex data to element properties like array for example:
el.range = ['2010-01-18', '2019-01-01'];
el.disabled = false;
Using the this.setAttribute
allows you to hook event listeners on attribute changes etc.
2. HTML Templates
<template>
<style></style>
<div></div>
<slot></slot>
<div></div>
</template>
const tmpl = document.querySelector('date-picker-template');
tmpl.content.cloneNode(/*deep*/true);
The fastest way to copy stuff over and over into your page because it is directly provided by the browser.
3. Shadow DOM
class MyEl extends HTMLElement {
constructor() {
super();
const myshadow = this.attachShadow({mode: 'open'});
myshadow.appendChild(tmpl.content.cloneNode(true));
}
}
4. HTML Imports
They are discontinued and discouraged. Everyone is focusing on javascript modules instead.
Support is pretty good
Using polyfill.