FOSCOS: Best Practices for Web Components

The following are a collection of best practices I came up on my own while dealing with Web Components in plain Javascript.

Full disclosure: I am becoming more and more intrigued by The Stackless Way

1. Fetch with ids

Retrieve children by their id, and this way only (they will have the same name as the component you want to access because of the Same identifier principle).

2. One component, one template

Define a new template for each and every component you're using, even if not needed right now. You will probably need it later.

3. Same identifier

Use the same identifier for the following things: – js file name (dashed) – component class name (camel case) – id of custom element in DOM (dashed) – id of template of a custom element (dashed) – name of the custom element (dashed)

For instance, MyCoolComponent is in a file named my-cool-component.js, with a custom element called my-cool-component, with an attribute id="my-cool-component, with a template with the attribute id="my-cool-component.

TODO for lists and stuff like that.

4. Communicate with attributes

Pass data to children setting attributes on them, and let them react with attributeChangedCallback.

TODO for child –> parent communication.

5. One way nesting (the only way that works)

Each Component should have nested components only inside its own shadow DOM. To do so, it must respect the One component, one template principle and then do the following:

	<!-- template of parent component (class ParentComponent) -->
       	<template id="parent-component">
            <!-- other template tags if needed -->
            <child-component id="child-component">
                <!-- slot tags if needed -->
            </child-component>
            <!-- other template tags if needed -->
        </template>

        <!-- template of child component (class ChildComponent) -->
        <template id="child-component">
            <!-- other tags if needed, or other components -->
        </template>

This requires a few lines of boilerplate code in each component constructor, for instance in the ParentComponent of the example above:

const shadowRoot = this.attachShadow({mode: 'open'});
let template = document.getElementById('child-component');
let templateContent = template.content;
shadowRoot.appendChild(templateContent.cloneNode(true));

The one outlined above is also the only way I could get nesting to work. Notice how, when inspecting the rendered DOM, each component doesn't have children outside the shadow DOM with the exception of tags with the slot attribute (which aren't really there anyway).

6. Style outside

Yes, I know that

const style = shadowRoot.appendChild(document.createElement("style"));
style.textContent = "@import url( '/components/bankster.css' );";

is ugly, but at least this way we can keep components smaller and more easily understandable.

Conclusions

While I still haven't used pure JS Web Components in a big production project, it's nice to investigate the possibility of not dealing with a framework. For small projects this will be my go to for the near future, let's see how this technology pans out.