// Tutorial //

Integrate Web Components with Your Vue.js App

Published on September 25, 2017
Default avatar
By Joshua Bemenderfer
Developer and author at DigitalOcean.
Integrate Web Components with Your Vue.js App

While we believe that this content benefits our community, we have not yet thoroughly reviewed it. If you have any suggestions for improvements, please let us know by clicking the “report an issue“ button at the bottom of the tutorial.

Even though the Web Components spec and implementations may not be considered to be mature by all, there are still quite a few interesting components and projects out there that you might find useful for your own purposes. That being said, Vue still offers a huge amount over plain’ol web components. (In fact, you can even make Web Components with Vue.) Well, why not have the best of both worlds? It’s actually really straightforward to use Web Components in Vue. Conditionals, properties, and even event bindings just keep working just like you’d expect them to.

Preparation

(This guide assumes you’ve started a quick Vue.js project with vue-cli and the webpack-simple template.)

Let’s go ahead an create a simple web component that illustrates properties and events with as little code as possible…

Behold: The ticking paragraph! Basically, it’s a wrapper for a paragraph. You set its text content through the content attribute. Oh, and it emits a tick event every half second or so for some reason. Yes, I lack creativity and / or the capability to come up with anything practical. Moving on.

ticking-paragraph.html
<template id="x-ticking-paragraph">
  <style>
    p {
      color: #42b983;
    }
  </style>
  <p id="renderTarget">
  </p>
</template>

<script>
  const currentScript = document.currentScript;

  customElements.define('x-ticking-paragraph', class extends HTMLElement {
    static get observedAttributes() { return ['contents'] }

    constructor() {
      super();
      let shadowRoot = this.attachShadow({mode: 'open'});
      const template = currentScript.ownerDocument.querySelector('#x-ticking-paragraph');
      const instance = template.content.cloneNode(true);
      shadowRoot.appendChild(instance);

      this.contents = '';

      setInterval(() => {
        this.dispatchEvent(new Event('tick'));
      }, 500);
    }

    set contents(value) {
      this._contents = value;
      this.shadowRoot.getElementById('renderTarget').innerText = this._contents;
    }

    get contents() {
      return this._contents;
    }

    attributeChangedCallback(name, oldValue, newValue) {
      this[name] = newValue;
    }
  });
</script>

I know, I know. I’m using the old HTML-imports style of component here because it feels more elegant… for a more civilized age. Also it totally looks like a Vue Single File Component doesn’t it? (Vue was inspired in part by the original Web Components spec.)

I’m not going to explain it all here, rather, if you’d like to see how to create a more proper Web Component for Modern Times™, we’ve covered that too.

Alright, so now we need to load it into the page somehow. We’ll also grab a polyfill along the way so this works in more browsers than just Chrome.

ticking-paragraph.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Vue + Web Components</title>
    <!-- Web Components Polyfill -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/webcomponentsjs/1.0.13/webcomponents-lite.js"></script>
    <!-- Loading our component -->
    <link rel="import" href="./ticking-paragraph.html">
  </head>
  <body>
    <div id="app"></div>
    <script src="/dist/build.js"></script>
  </body>
</html>

Now that we’ve done all that, we can finally use our component in Vue as stated by the title directly following this paragraph.

Using Your Component In Vue

Really the only thing we have to do to make Vue work with this component is whitelist it in Vue.config.ignoredElements. This just tells Vue that the element / component comes from a source that Vue doesn’t know about, and keeps the compiler from complaining.

src/main.js
import Vue from 'vue';
import App from './App.vue';

Vue.config.ignoredElements = [
  'x-ticking-paragraph'
]

new Vue({
  el: '#app',
  render: h => h(App)
});

Now, use the component like any other! Reactivity stays intact!

src/App.vue
<template>
  <div id="app">
    <h1>Vue ❤ Web Components</h1>
    <x-ticking-paragraph :contents="paragraphContents" @tick="logTick"></x-ticking-paragraph>
  </div>
</template>

<script>
export default {
  data() {
    return {
      paragraphContents: `I'm data from Vue rendering in a Web Component!`
    }
  },

  methods: {
    logTick() {
      console.log(`The paragraph ticked again. >_>`)
    }
  }
}
</script>

The best part is, you don’t need to make any changes to the Web Component or to your Vue app to somehow force them to grudginly work together. It works right out of the box! In fact, you can even integrate elements that internally use other frameworks like Polymer or React.

Now, how useful this ends up being to you depends entirely on whether or not you are (or want to be) using Web Components alongside Vue. That said, if nothing else, it does indicate that if Web Components are the future, Vue is future-proof.


Want to learn more? Join the DigitalOcean Community!

Join our DigitalOcean community of over a million developers for free! Get help and share knowledge in our Questions & Answers section, find tutorials and tools that will help you grow as a developer and scale your project or business, and subscribe to topics of interest.

Sign up
About the authors
Default avatar
Developer and author at DigitalOcean.

Still looking for an answer?

Was this helpful?
Leave a comment

This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!