Building Web Components with your favorite JavaScript framework

JavaScript frameworks have become an integral part of our web development toolkit. The world of JavaScript frameworks has grown bigger and more complex. However as of today we see a phew major players like React, Angular and Vue cementing their place in the industry. All of these are powerful complex technologies with a steep learning curve. Committing to either of the frameworks creates a major dependency for your organisation and a requirement for a specialised work force capable of efficiently using the framework of choice. In addition going with one of these frameworks locks you in the Frameworks ecosystem making it more challenging to take advantage of developments in other JavaScript environments. As we know, the world of web development is an ever changing environment and committing to a certain technology without a backup plan can have serious implications for the future.

Today I would like to explore with you one possible solution for how we can introduce a "backup plan" for "safeguarding" the future of our Frontend code and potentially take advantage of developments across different ecosystems. This potential Solution is to build Web Components using our chosen JavaScript framework. Lets explore some of the options that we have today for the 3 top JS frameworks.

First lets look at what are web components and why, as responsible web development professionals, we should be interested in them.

What are Web Components?

Web Components are a collection of different technologies allowing web developers to create reusable HTML\JavaScript components with their functionality completely separated from the application that uses them. Very importantly Web Component technologies are standardized technologies for building reusable custom components thus paving a way for creating framework and browser agnostic custom components for the web.

Web components encapsulate the following 4 technologies:

  • HTML imports - a way to import html documents into other documents
  • HTML templates - a way to create portions of html code on the page that will not be rendered initially but can be used by JavaScript code later in the applications logic
  • Custom elements - native APIs for building your own HTML elements
  • Shadow DOM - provides the ability to encapsulate the logic of your component and the styles completely away from the rest of the application

Why Web Components?

One of the reasons most of us got excited with using JS frameworks such as Angular and React is the ability to split our application into simple reusable components. Web components promise to give us the ability to do this natively without worrying about third party dependencies, framework feature deprecation or employee specific skills. Web components allow to wrap UI components into isolated bits of UI logic via Custom Elements with Shadow DOM technologies and HTML templates making it possible to safely mix and match different component libraries. This opens tons of possibilities for expanding your audience when you want to share your work and significantly reduces complexity for implementing your work by other people. Finally web components are native technologies that don't require third party dependencies which should help in reducing your JavaScript library footprint and improve performance of complex UI components.

Browser Support

As of today Chrome, Opera, Firefox, Safar and Edge fully support web components.

Source: webcomponents.org

Most of the browser support limitations can be overcome with poly-fills on which we have to rely anyway when using React, Angular or Vue with older browsers.

Web Components limitations

There are a number of limitations with using pure web components.

Slow pace of standard adoption

As we know from the past, Web Standards can be slow to adopt. This might introduce a delay in development of new features in this space.

Mixed browser support

The variation in browser support has significantly improved and today, if you are building components for "Evergreen" browsers, you don't even need to think about it. However if you are building applications for the enterprise, which might still need to support older versions of IE, you should carefully analyse the browser support limitations that you might have to face.

Relatively complex and verbose API

The API is still quite verbose and might be hard to grasp.

Requires going back to pure Vanilla JavaScript for building complex UX

Building Web components using Vanilla JavaScript can be hard but it could be a positive side effect as it could help "Framework Developers" get back to JavaScript roots and write more conscientious and performant code.

Using Web components with React, Angular and Vue

The good news is that all of the three JavaScript framework Titans have support for using web components out of the box.

In React you can directly use web components in your jsx templates

class HelloMessage extends React.Component {
  render() {
    return (
      <div>
        Hello <x-search>{this.props.name}</x-search>!
      </div>
    );
  }
}

Source: React Docs

In Vue you can use web components by adding them to the ignored elements list (https:\alligator.io\vuejs\vue-integrate-web-components)

Vue.config.ignoredElements = ["x-ticking-paragraph"];

In Angular you can use web components by enabling support for the CUSTOM_ELEMENT_SCHEMA (https:\alligator.io\angular\using-custom-elements)

@NgModule({
  declarations: [AppComponent],
  schemas: [CUSTOM_ELEMENTS_SCHEMA],
  imports: [BrowserModule, FormsModule],
  providers: [],
  bootstrap: [AppComponent],
})
export class AppModule {}

Now that we know that we can safely use web components in our existing single page applications it is time to figure out how to build them with a minimal impact to our time and efficiency. This is where I have discovered that we can actually use our favorite framework to build Web Components.

Using React, Vue and Angular to build web components

The example component is a simple "likes counter" with 2 buttons "Like" to bring the likes counter up and "Dislike" to bring the likes counter down

First lets have a quick look at how to build this simple Web Component natively. I have used stackblitz to demonstrate how to do it. You can follow the code or check this Mozilla article for step by step instructions

Embed the following here https://stackblitz.com/edit/web-components-article-js?file=index.html

In the next part we will look at how to build web components with react, Angular and Vue

React

Next lets build the above component using React.

https://stackblitz.com/edit/web-components-article-react?embed=1&file=VoteCounter.js

As you can see nothing special here, we are extending the React.Component class, adding local state to store the "totalCount" value and adding like and dislike methods to handle the buttons functionality. All then gets rendered using the render function. We then have an App Component that renders our VoteCounter component

Things get a bit more complex when it comes to wrapping React component as a web component. React does not have a native way of wrapping React Components as Web Components. I have only found one library that claims to be capable of doing it. The library is called react-web-component developed by spring-media. Here is how you would convert the above component into a web component using this library:

install the react-web-component package
npm install react-web-component

Now create a React component using the code from the previous stages Import the react-web-components library Add some web component specific life-cycle hooks Wrap the component using the method from the React web components library Now we need to build this component as a standalone JS file Ado the build steps

This library did indeed create a webcomponent but I have hit some big limitations, one of them not being able to pass props from HTML to the react component. There are multiple issues reported on this problem #123, #234, #456. Unfortunately the library does not seem to be actively maintained and these issues have been open for quite a while now.

Another approach to use web components with React is to actually use React inside the web component. This will require React to be loaded alongside the web component.

Vue

Vue supported the concept of converting Vue components into native Web Components for a while and the process is relatively straight forward.

Vue has a native library called @vue/web-component-wrapper. The library requires vue cli V3:

  • Create a new project using the cli
vue create web-component-article-vue
  • Create a new component
src/components/RsvpCounter.vue
  • Use the @vue/web-component-wrapper library in the main.js file to wrap the component as a webcomponent
  • Use the window.customElements.define function to add the web component to the component registry
  • Final step is to package the Vue component as a standard webcomponent. We can use the powerful vue cli for this by running the following command
vue-cli-service build --target wc

Note: for the last step I have added a dedicated main.wc.js file containing just the code required for the web component and I have added the command was an npm script Note: When running the web component in dev mode styles for the wrapped web component will not work. Everything works fine after building the web component

https://vuejsdevelopers.com/2018/05/21/vue-js-web-component/ .........

Angular

Angular version 6 has introduced a way to build web components using the @angular\elements package. Later versions of Angular introduced further improvements to @angular\elements. Here is how you would build a web component in Angular 8

This is how we would build the above component using @angular/elements

Setup a new project ng new angular-app Generate a new component ng g c RsvpCounter Add the @angular/elements library ng add @angular/elements Add the @webcomponents/webcomponentsjs library npm install @webcomponents/webcomponentsjs Wrap the Angular component as a webcomponent in the AppModule constructor

export class AppModule {
  constructor(injector: Injector) {
    const custom = createCustomElement(HelloWorldComponent, {injector: injector});
  }
}

Now add the web component to custom elements

customElements.define("app-hello-world", custom);

Similar to the customElements issue in using react in web components, you might require a polyfill for custom elements to work.

@webcomponents/webcomponentsjs`

Simply add the polyfill to your polyfils.ts file

import '@webcomponents/webcomponentsjs/custom-elements-es5-adapter.js';`

The above steps will make the component usable in Angular components allowing things like dynamically injecting the component using vanilla JavaScript.

Now lets look at how to package our component as a a standalone web component.

First lets add a new module that will only contain our web component

[Example code required]

Now lets add a new config to angular-json for building this new module

[Example code required]

Now lets add a build command that will perform the following:

  • Build the angular app specifying the new module as the 'main' file
  • Concatenate the built files
  • Output the final build to a webcomponent folder
ng build angular-app --prod --output-hashing=none --main=helloworld.wc.module.ts && cat dist/angular-app/runtime.js dist/angular-app/polyfills.js dist/angular-app/scripts.js dist/angular-app/main.js > preview/angularapp.js

Note:

For windows users there is a node implementation of cat nodecat. Alternatively you could also use this package ngx-build-plusngx-build-plus that extends the Angular CLI and provides a mechanism for outputting single js bundles

..

The Angular team has been doing a lot of work in further improving efficiency, and the bundle size produced by the Angular rendering engine to a point where a hello world App now builds to a bundle that is only 27KB in size. But the most exciting feature is the new version of Angular rendering engine code named Ivy. It has been introduced as an optin feature in Angular 8. The engine has been refactored to leverage full use of tree shaking techniques making it possible to only include the bare necessary parts of your application down to a rendering instruction level. This makes the final bundle even smaller giving even more room for producing standalone web components using Angular.

Conclusion

As we can see all the three JavaScript framework giants support Web Components one way or another, it is clear that Vue and Angular see it as an integral peace that will help the frameworks stand the course of time. Vue had good web component support since version 3 of the Vue CLI and has the most straightforward and easy to use tooling. The Angular team has been putting a lot of efforts into this area as well and significantly reduced the configuration required for building web components. React can easily consume web components but is not really fit for building them. It seems that the React community does not feel like web components are important to them so i don't think we will get native like support any time soon.

Who knows maybe one day all of the components we will be building will be web components. So why not start experimenting now and have one more skill in our portfolio for helping us deal with the ever changing web development landscape.