Get started with Vue web components

Roy Prins
4 min readNov 21, 2018

--

I found the official documentation somewhat wanting, so here is a short guide to building web-components with Vue. It’s actually pretty easy to set up your environment correctly using Vue CLI 3.

Let’s build a web component named <my-web-component> that consumes a msg attribute. Nothing fancy. Here are the steps:

  1. Set up the project
  2. Start with a Vue component
  3. Wrap up as a webcomponent
  4. Build for production
  5. Inspect the results
  6. Style your component
  7. Applying styles in development
  8. Results and source code

1. Set up the project

Make sure you have the Vue command line tools installed.

npm install -g @vue/cli
vue --version
# should be 3.x

Create a new Vue project with default settings.

vue create web-component-project
> default (babel, eslint)
cd web-component-project

The default Vue environment comes with the vue-cli-service plugin. This plugin can serve your project with a development webserver that supports hot-module-replacement or building your web component for production.

2. Start with a Vue component

Add the following Vue component to src/components. It should be no different from a normal Vue component. It takes a msg property and interpolates it to the template.

src/components/MyWebComponent.vue

3. Wrap up as a web component

To make your Vue component into a web component, we can simply wrap it in the @vue/web-component-wrapper library. This wrapper interfaces it with web component APIs and automatically proxies properties, attributes, events, and slots.

To serve the web component in development, we can do the wrapping in the main.js file and use the component in the index.html file. Update these files to read:

src/main.js
public/index.html

Now run the development server to view the my-web-component in action:

npm run serve

Visit 127.0.0.1:8080 to see the page. It should look like this:

4. Build for production

The production build will be slightly different. Without using main.js, the vue-cli-service itself will wrap the web component by using the --target wc option.

Update the package.json file to read:

"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build --target wc --name my-web-component ./src/components/MyWebComponent.vue",
}

This will create a build that is optimized for deployment and does not include the core vue library. The rationale is that there can typically be multiple web components on a website and packaging the Vue library with every instance would be a waste of resources. Let’s build it:

npm run build

The dist folder should now have at least the following files:

  • my-web-component.js
  • demo.html

The html file demonstrates the use of your web component. Add a msg attribute to <my-web-component> and open it in the browser. Note that the Vue library is added separately.

dist/demo.html

5. Inspect the results

Inspect my-web-component.js to see how it is constructed. Near the end of the file, you should see the following line:

window.customElements.define('my-web-component', vue_wc_wrapper(external_Vue_default.a, MyWebComponentshadow))

This uses the define() method of the browser’s custom elements registry to register our custom my-web-component element. But first the Vue component is wrapped inside a web component wrapper, just as we did manually for the development build.

The browser DOM should reflect the shadow DOM that was inserted inside the custom element. Note the #shadow-root

6. Style your component

Styles can be added just as you would on a conventional Vue component with the <style> tag. The style will be automatically scoped to the web component and none of the document styles will bleed into the web component, vice-versa (this is a feature of the shadow DOM).

src/components/MyWebComponent.vue
npm run build

The style is incorporated into the web component, as we should expect.

7. Applying styles in development

The same is not not automatically true for the development setup. Contrary to the results of wrapping in the build script, our custom code in the main.js file will add the styles to the document head. Since the shadow DOM is by nature isolated from the document, the styles will not be applied to the web component. Observe by running:

npm run serve

The solution as described here, is to set the correct shadowMode option in vue-loader and vue-style-loader. Turns out shadowMode is set false by default in a Vue CLI project, but we can tweak that in vue.config.js.

First inspect the Webpack config to determine which loaders to change:

# run at project root
vue inspect

The result should resemble this. From that, we can extract a list of occurrences of the shadowMode: false option. We can use this list to update the webpack config through vue.config.js (create it in the project root, if it not already exits).

project-root/vue.config.js

With this in place, once again run the development server:

npm run serve

The Vue webpack config would be helped by having a default setting for the shadowMode, so you would normally have to configure only that value. Check the feature request for the status.

8. Results and source code

Live instance: https://snirp.github.io/vue-web-component/

Source code: https://github.com/snirp/vue-web-component

Credit

I received some great help from tony19 at stackoverflow.com in figuring out the configuration for the development setup.

--

--