Web Components in SPFx world: Vue vs Stencil

This year I hear “Web Components” term from here and there more frequently. What exactly is Web Components?

Web components are a set of web platform APIs that allow you to create new custom, reusable, encapsulated HTML tags to use in web pages and web apps. Custom components and widgets build on the Web Component standards, will work across modern browsers, and can be used with any JavaScript library or framework that works with HTML.

In other words, web components are reusable pieces of HTML and JavaScript, which can be used in modern browsers, and what is even more important, can be consumed by JavaScript frameworks. Today we have a few web frontend libraries for building web interfaces and SPAs. All of them uses concept of components, however components from one library can’t be easily ported to another one. It looks weird, because in the end that’s just portion of html + styles + logic (JavaScript). However today, in 2018 we can’t reuse building blocks from one library in another. It’s a shame. Web components solve that issue. They can be reused across JavaScript frameworks. Check out webcomponents site for more information.

Web components is a set of W3C standards, which describe how everything fits together in browser. Not all browsers fully supports all web components’ specifications. Google Chrome has full support, Firefox is developing a few remaining things and Edge has the worst support:

However for all browsers with lack of some features polyfills are available.

How web components can be used in SharePoint Framework? That’s not so difficult. At first, we need to create a web component. There a few libraries for building web components – Polymer, Stencil, Vue, X-Tag, Skate and some others. While almost all are intended for web components building model only, Vue is a new beast here. With latest Vue CLI 3.0 release it’s possible to create a web component from any .vue single-file-component. That’s very interesting and existing feature!

In this I post I compare what it takes to create a web component using Stencil vs Vue and reuse it in SharePoint Framework. Why these two? Stencil looks really good and solid among other web components related  libraries. It has very intuitive API and some similarities to React, it’s developed by Google, and Google itself created Ionic 4 library with Stencil. Ionic 4 is a big library with lots of UI components. And Vue is here simply because it’s different, it’s not a web component building library, but it’s the first library from “big 3”, which added web components transformation tools.

By the way Waldek Mastykarz posted a great article on a web components topic. He used Stencil and created Office UI Fabric button as web component. Check it out as well!

The code for this posts is on GitHub. In readme you can also find steps required, in order to compile and test samples. Samples wok well in Chrome, so please use it for testing.

Let’s start with Stencil implementation first

I’ve created very simple toggle button, with nice styling.

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

@Component({
  tag: 'stencil-toggle',
  styleUrl: 'toggle.css',
  shadow: true
})
export class Toggle {

  @Prop() id: string;

  render() {
    return (
      <div class="onoffswitch">
        <input type="checkbox" name={this.id} id={this.id} class="onoffswitch-checkbox" />
        <label class="onoffswitch-label" htmlFor={this.id}></label>
      </div>
    );
  }
}

You see how easy it’s to create a web component with Stencil – only a few lines of code. At the same time the code is clean and self-explanatory. For more info about Stencil, checkout docs. From this point we should bundle our web component with Stencil and distribute to npm. For simplicity I use npm link command to store package locally.

Once web component is published to npm, we can use it any web framework, including SPFx. To make it work, we should explicitly register our web component with Stencil’s defineCustomElements method. In your SPFx web part, you should perform below steps:

  1. Import defineCustomElements function from your web components.
  2. Register your web component.
  3. Use it in html.

Below is the code which does exactly that:

import { defineCustomElements } from "stencil-toggle"; // <-- #1

export default class HelloWorldWebPart extends BaseClientSideWebPart<IHelloWorldWebPartProps> {

  public onInit(): Promise<void> {
    defineCustomElements(window); // <-- #2
    return Promise.resolve();
  }
  public render(): void {
    this.domElement.innerHTML = `
      <p class="${ styles.subTitle }">This is a toggle button built as web component with <b>Stencil</b>:</p>
      <stencil-toggle id="stencil-toggle"></stencil-toggle>`; // <-- #3
    }
   // your other code 

Basically this it for Stencil. The component we built is fully functional in SPFx.

Let’s go to Vue

If you familiar with Vue, the code below looks familiar for you:

<template> <div class="onoffswitch"> <input type="checkbox" :name="id" :id="id" class="onoffswitch-checkbox" /> <label class="onoffswitch-label" :for="id"></label> </div> </template> <script lang="ts"> import Vue from "vue"; export default Vue.extend({ name: "vue-toggle", props: { id: String } }); </script>

<styles>…</styles>

That’s just a regular Vue code, however with below magic command it automatically transforms to web component:

npx vue-cli-service build --target wc --name vue-toggle src/components/Toggle.vue

Now we have our web component built with vue, let’s add it to SPFx web part. For vue we should perform a bit more steps:

  1. Add Vue as external dependency.
  2. Modify webpack pipeline to provide Vue as global object to all modules. We need this step, because all components, which are built with Vue, have dependency on Vue, thus it’s impossible to have web component built with Vue without Vue on a page.
  3. Import component in web part.
  4. Use it in html.

To add Vue as external dependency, modify config.json file and add new externals:

"externals": {
    "vue": {
      "globalName": "vue",
      "path": "https://cdn.jsdelivr.net/npm/vue@2.5.17/dist/vue.runtime.min.js"
    }
  },

Then adjust web pack pipeline and provide Vue variable in modules via webpack.Provide plugin (modify gulpfile.js):

const merge = require('webpack-merge');
const webpack = require('webpack');

build.configureWebpack.setConfig({
    additionalConfiguration: function (config) {
        var vueConfig = {
            plugins: [
                new webpack.ProvidePlugin({
                  Vue: 'vue',
                  'window.Vue': 'vue'
                })
              ]
        };

        return merge(config, vueConfig);
    }
});

build.initialize(gulp);

Now we are good to go and are eligible to use web component in code:

import "./../../../../vue/vue-toggle/dist/vue-toggle.min"; // <-- #3

export default class HelloWorldWebPart extends BaseClientSideWebPart<IHelloWorldWebPartProps> {

  public render(): void {
    this.domElement.innerHTML = `
	  <p class="${ styles.subTitle }">And this is a toggle button built as web component with <b>Vue</b>:</p>
	  <vue-toggle id="vue-toggle"></vue-toggle>`;  // <-- #4
  }

Comparison

You see, it’s relative easy to create and consume a web component built with Stencil or Vue in SPFx. However there are some key differences, you should be aware of. I’ve built a comparison table for better handling:

  Vue   Stencil
Polyfills Doesn’t handle polyfills for you, you should  explicitly include polyfills for all unsupported features. Automatically detects missing features in browser and dynamically adds polyfill.
External dependenciesWeb components require Vue to be available globally.Doesn’t require any external dependencies
Bundle sizeSimple toggle component (~10KB) + global Vue = ~70KB Simple toggle component (~3KB) + Stencil stuff = ~30KB
Complexity when using with SPFxVery simple, however requires one-time webpack pipeline adjustmentVery simple

In general Stencil looks a bit nicer, because that’s a library intended for building web components, which is not the case for Vue. Which one would I select for web components? Of course it depends. If you invested a lot of time in Vue, if you know Vue very well, then go ahead with Vue approach. For all other cases I would select Stencil.

Final thoughts

I believe that in future web components will be the standard for building web interfaces. “Frameworks hell” is not a good thing, especially today in 2018. Things are progressing slowly and separate libraries still rule the web frontend world. However we have a few options for building web components today without much pain. Those components can be easily imported into SPFx solution later on. Probably that’s the future? What do you think?