Web Components

can do that ?!

Why

Web Components ?

a very big company

🖥 Many applications

🛠 Many technologies

👥 Many teams

Need consistency

Need interoperability

Need stability

In big companies

At an UI level

A Design system

👇🏼

In big companies

At a technical level

Web components

👇🏼

Framework

Web components

orchestrate

plays

Agenda

2.

Use cases

3.

What is next ?

1.

The 4 trends for Web Components

Vincent Ogloblinsky

Frontend software architect

The

4 trends

for

Web Components

Librairies

Compilers

Backporters

Framework

Librairies

2 approaches :

class  based

functional based

Librairies - Class based

import { 
    LitElement,
    html,
    property,
    customElement
} from 'lit-element';

@customElement('simple-greeting')
export class SimpleGreeting extends LitElement {
  @property() name = 'World';

  render() {
    return html`<p>Hello, ${this.name}!</p>`;
  }
}
<simple-greeting name="Everyone"></simple-greeting>

Librairies - Class based

import {Slim} from 'slim-js'
import {
    tag,
    template,
    attribute
} from 'slim-js/Decorators'

@tag('simple-greeting')
@template('<p>Hello, {{name}}!</p>')
class MyTag extends Slim {
  @attribute
  name
}
<simple-greeting name="Everyone"></simple-greeting>

Librairies - Functional based

import 
    Element, { html }
from '@skatejs/element-lit-html';

export default class extends Element {
  static get props() {
    return {
      name: String
    };
  }
  render() {
    return html`
      <p>Hello, ${this.name}!</p>
    `;
  }
}
<simple-greeting name="Everyone"></simple-greeting>

Librairies - Functional based

import { html, define } from 'hybrids';
​
export const HelloWorld = {
  name: '',
  render: ({ name }) => html`
    <p>Hello, ${name}</p>
  `,
};
​
define('simple-greeting', HelloWorld);
<simple-greeting name="Everyone"></simple-greeting>

Framework

import { define, WeElement, html, render } from 'omi'

define('simple-greeting', class extends WeElement {

  render(props) {
    return html`<p>{props.text}</p>`
  }

});
<simple-greeting name="Everyone"></simple-greeting>

Compilers

<!-- simple-greeting.riot -->
<p>{ props.message }</p>
import { register, mount, component } from 'riot';
import simpleGreeting from './simple-greeting.riot';

// register the my-component as global component
register('simple-greeting', simpleGreeting);

// find all the DOM nodes called `<simple-greeting>` and
// mount them with the component previously registered
mount('simple-greeting');
<simple-greeting name="Everyone"></simple-greeting>

Compilers

<p>{name}</p>
<script>
    export default {
        tag: 'simple-greeting',
        props: ['name']
    }
</script>

/**
 * -> Compile it with Svelte CLI
 */
<simple-greeting name="Everyone"></simple-greeting>

Compilers

import { h, Component, Prop } from '@stencil/core';

@Component({
  tag: 'simple-greeting'
})
export class SimpleGreeting {
  @Prop() name: string;

  render() {
    return <p>{this.name}</p>;
  }
}
<simple-greeting name="Everyone"></simple-greeting>

Backporters

import { Component, Input } from '@angular/core';

@Component({
  selector: 'simple-greeting',
  template: `
      <p>{{ name }}</p>
  `
})
export class SimpleGreeting {
  @Input() name: string;
}
<simple-greeting name="Everyone"></simple-greeting>
import { ɵrenderComponent } from '@angular/core';
import { SimpleGreeting } from './simple-greeting.component';

export class SimpleGreetingElement extends HTMLElement {
    private comp: SimpleGreeting;

    constructor() {
        super();
        this.comp = ɵrenderComponent(SimpleGreeting, { host: this });
    }
}

Backporters

<template>
  <p>{name}</p>
</template>

<script>
    export default {
        name: 'SimpleGreeting',
        props: ['name']
    }
</script>

/**
 * -> Compile it with Vue CLI
 */
<simple-greeting name="Everyone"></simple-greeting>

Backporters

import { WidgetBase } from '@dojo/framework/widget-core/WidgetBase';
import customElement from '@dojo/framework/widget-core/decorators/customElement';
import { v } from '@dojo/framework/widget-core/d';
import { ThemedMixin } from '@dojo/framework/widget-core/mixins/Themed';

@customElement({
    tag: 'simple-greeting',
    events: [],
    attributes: ['name'],
    properties: []
})
export default class SimpleGreeting extends ThemedMixin(WidgetBase) {
    protected render() {
        const { name } = this.properties;
        return v('s', {}, [name]);
    }
}

/**
 * -> Compile it with Dojo CLI
 */
<simple-greeting name="Everyone"></simple-greeting>

Backporters

Benchmark

Make a choice

Warning !

Take care of Developer eXperience (DX)

versus

User eXperience (UX)

Use-cases

1 - Design System

Centralized library of components that can be shared across teams and projects to simplify design and development while ensuring consistent brand experiences, at scale.

Design System

Design System - Advantages

📉 Design debt

📉🤜🏻🤛🏻 Collaboration and communication problems

📉 Repetitive work

Consistency within a product family

Design System - Examples

Design System - How to ?

Balance consistency with flexibility

Component describes visual language & functional behavior

Journey which designers and developers take together

Design System - How to ?

Code with Web Components

...

Design System - How to ?

Tooling with open-wc

Design System - How to ?

Documentation with Storybook

Design System - How to ?

Share them with

Bit or npm

2 - Standalone widget

3 - Framework migration

Most of the time

⛪️ Catholic wedding with framework

🏗 Architecture is based on a framework

Framework migration

?

?

?

?

Framework migration

2 strategies :

💣 Big bang

🏗 Incremental

Framework migration

WC

WC

WC

WC

Framework migration

Web component integration

Migrate existing component to WC

Check your framework's WC support

Framework migration

Check framework WC support

Framework migration

Feedbacks from companies

Warning on internal UI libraries and compatibility

Transition time between old & new screen

Stop coupling a lot

It just "works"

4 - Micro frontends

The idea behind Micro Frontends is to think about a website or web app as a composition of features which are owned by independent teams. Each team has a distinct area of business or mission it cares about and specialises in. A team is cross functional and develops its features end-to-end, from database to user interface. (micro-fontend.org)

Micro frontends

Micro frontends

Strategies

Fragment concept (Zalando) - Project Mosaic

Single-spa - Framework which connects frameworks with iframes

Web Components

Micro frontends

Starting questions

How big is your application in terms of team and code ?

Can you divide your app into small pieces by domain accessory ?

How easy can you release small features for your application ?

What's next ?

Move from static to dynamic template, also during update

<template id="person">
  <section>
      <h1>{{name}}</h1>
      Email: <a href="mailto:{{email}}">{{email}}</a>
  </section>
</template>
let template = document.querySelector('#person');
let instance = template.createInstance({
    name: "Ryosuke Niwa",
    email: "rniwa@webkit.org"
});

CSS Shadow parts

Style elements inside Shadow DOM

<x-foo>
  #shadow-root
    <div part="some-box"><span>...</span></div>
    <input part="some-input">
    <div>...</div> /_ not styleable _/
</x-foo>
x-foo::part(some-box) { ... }

Chrome 74

Create stylesheet objects from script & remove the need for declarative style elements

const myElementSheet = new CSSStyleSheet();

class MyElement extends HTMLElement {
  constructor() {
    super();
    const shadowRoot = this.attachShadow({ mode: "open" });
    shadowRoot.adoptedStyleSheets = [myElementSheet];
  }

  connectedCallback() {
    if (myElementSheet.cssRules.length == 0) {
       myElementSheet.replaceSync(styleText);
    }
  }
}

ECMAScript 6 native module for HTML & CSS

<script type="module">
    import {content} from "import.html"
    document.body.appendChild(content);
</script>
<div id="blogPost">
    <p>Content...</p>
</div>
<script type="module">
    let blogPost = import.meta.document.querySelector("#blogPost");
    export {blogPost}
</script>

ECMAScript 6 native module for HTML & CSS

import {content} from "import.html"

import styles from './styles.css';

class MyElement extends HTMLElement {
  constructor() {
    this.attachShadow({mode: open});
    // push() doesn't actually exist yet     
    this.shadowRoot.moreStyleSheets.push(styles);
    this.shadowRoot.appendChild(content);
  }
}

Chrome 73

Namespacing for Custom Element definition

// Create a new registry that inherits from the global registry
const myRegistry = new CustomElementRegistry(window.customElements);

// Use the local registry when creating the ShadowRoot
element.attachShadow({mode: 'open', customElements: myRegistry});
// Define a trivial subclass of XFoo so that we can register it ourselves
class MyFoo extends XFoo {}

// Register it as `my-foo` locally.
myRegistry.define('my-foo', MyFoo);

// the definiton of my-foo will be resolved from the custom registry
const myFoo = this.shadowRoot.createElement('my-foo');
element.shadowRoot.appendChild(myFoo);

Conclusion

Analyze your application(s)

Start small, mix 1 by 1

Choose the correct scope of your WC(s)

Take care of performance issues

Automate everything

Resources

Stencil for Production design systems

https://stenciljs.com/design-systems

Resources

UI in Microservices World — Micro Frontends pattern and Web Components

https://medium.com/@witek1902/ui-in-microservices-world-micro-frontends-pattern-and-web-components-23607a569363

Keep It Simple Stupid

#UseThePlatform

Thanks for listening !

Ask me anything

Made with Slides.com