# Vue and Web Components {#vue-and-web-components} [Web Components](https://developer.mozilla.org/en-US/docs/Web/Web_Components) is an umbrella term for a set of web native APIs that allows developers to create reusable custom elements. We consider Vue and Web Components to be primarily complementary technologies. Vue has excellent support for both consuming and creating custom elements. Whether you are integrating custom elements into an existing Vue application, or using Vue to build and distribute custom elements, you are in good company. ## Using Custom Elements in Vue {#using-custom-elements-in-vue} Vue [scores a perfect 100% in the Custom Elements Everywhere tests](https://custom-elements-everywhere.com/libraries/vue/results/results.html). Consuming custom elements inside a Vue application largely works the same as using native HTML elements, with a few things to keep in mind: ### Skipping Component Resolution {#skipping-component-resolution} By default, Vue will attempt to resolve a non-native HTML tag as a registered Vue component before falling back to rendering it as a custom element. This will cause Vue to emit a "failed to resolve component" warning during development. To let Vue know that certain elements should be treated as custom elements and skip component resolution, we can specify the [`compilerOptions.isCustomElement` option](/api/application#app-config-compileroptions). If you are using Vue with a build setup, the option should be passed via build configs since it is a compile-time option. #### Example In-Browser Config {#example-in-browser-config} ```js // Only works if using in-browser compilation. // If using build tools, see config examples below. app.config.compilerOptions.isCustomElement = (tag) => tag.includes('-') ``` #### Example Vite Config {#example-vite-config} ```js // vite.config.js import vue from '@vitejs/plugin-vue' export default { plugins: [ vue({ template: { compilerOptions: { // treat all tags with a dash as custom elements isCustomElement: (tag) => tag.includes('-') } } }) ] } ``` #### Example Vue CLI Config {#example-vue-cli-config} ```js // vue.config.js module.exports = { chainWebpack: (config) => { config.module .rule('vue') .use('vue-loader') .tap((options) => ({ ...options, compilerOptions: { // treat any tag that starts with ion- as custom elements isCustomElement: (tag) => tag.startsWith('ion-') } })) } } ``` ### Passing DOM Properties {#passing-dom-properties} Since DOM attributes can only be strings, we need to pass complex data to custom elements as DOM properties. When setting props on a custom element, Vue 3 automatically checks DOM-property presence using the `in` operator and will prefer setting the value as a DOM property if the key is present. This means that, in most cases, you won't need to think about this if the custom element follows the [recommended best practices](https://web.dev/custom-elements-best-practices/). However, there could be rare cases where the data must be passed as a DOM property, but the custom element does not properly define/reflect the property (causing the `in` check to fail). In this case, you can force a `v-bind` binding to be set as a DOM property using the `.prop` modifier: ```vue-html ``` ## Building Custom Elements with Vue {#building-custom-elements-with-vue} The primary benefit of custom elements is that they can be used with any framework, or even without a framework. This makes them ideal for distributing components where the end consumer may not be using the same frontend stack, or when you want to insulate the end application from the implementation details of the components it uses. ### defineCustomElement {#definecustomelement} Vue supports creating custom elements using exactly the same Vue component APIs via the [`defineCustomElement`](/api/custom-elements#definecustomelement) method. The method accepts the same argument as [`defineComponent`](/api/general#definecomponent), but instead returns a custom element constructor that extends `HTMLElement`: ```vue-html ``` ```js import { defineCustomElement } from 'vue' const MyVueElement = defineCustomElement({ // normal Vue component options here props: {}, emits: {}, template: `...`, // defineCustomElement only: CSS to be injected into shadow root styles: [`/* inlined css */`] }) // Register the custom element. // After registration, all `` tags // on the page will be upgraded. customElements.define('my-vue-element', MyVueElement) // You can also programmatically instantiate the element: // (can only be done after registration) document.body.appendChild( new MyVueElement({ // initial props (optional) }) ) ``` #### Lifecycle {#lifecycle} - A Vue custom element will mount an internal Vue component instance inside its shadow root when the element's [`connectedCallback`](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements#using_the_lifecycle_callbacks) is called for the first time. - When the element's `disconnectedCallback` is invoked, Vue will check whether the element is detached from the document after a microtask tick. - If the element is still in the document, it's a move and the component instance will be preserved; - If the element is detached from the document, it's a removal and the component instance will be unmounted. #### Props {#props} - All props declared using the `props` option will be defined on the custom element as properties. Vue will automatically handle the reflection between attributes / properties where appropriate. - Attributes are always reflected to corresponding properties. - Properties with primitive values (`string`, `boolean` or `number`) are reflected as attributes. - Vue also automatically casts props declared with `Boolean` or `Number` types into the desired type when they are set as attributes (which are always strings). For example, given the following props declaration: ```js props: { selected: Boolean, index: Number } ``` And the custom element usage: ```vue-html ``` In the component, `selected` will be cast to `true` (boolean) and `index` will be cast to `1` (number). #### Events {#events} Events emitted via `this.$emit` or setup `emit` are dispatched as native [CustomEvents](https://developer.mozilla.org/en-US/docs/Web/Events/Creating_and_triggering_events#adding_custom_data_%E2%80%93_customevent) on the custom element. Additional event arguments (payload) will be exposed as an array on the CustomEvent object as its `detail` property. #### Slots {#slots} Inside the component, slots can be rendered using the `` element as usual. However, when consuming the resulting element, it only accepts [native slots syntax](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_templates_and_slots): - [Scoped slots](/guide/components/slots#scoped-slots) are not supported. - When passing named slots, use the `slot` attribute instead of the `v-slot` directive: ```vue-html
hello
``` #### Provide / Inject {#provide-inject} The [Provide / Inject API](/guide/components/provide-inject#provide-inject) and its [Composition API equivalent](/api/composition-api-dependency-injection#provide) also work between Vue-defined custom elements. However, note that this works **only between custom elements**. i.e. a Vue-defined custom element won't be able to inject properties provided by a non-custom-element Vue component. #### App Level Config {#app-level-config} You can configure the app instance of a Vue custom element using the `configureApp` option: ```js defineCustomElement(MyComponent, { configureApp(app) { app.config.errorHandler = (err) => { /* ... */ } } }) ``` ### SFC as Custom Element {#sfc-as-custom-element} `defineCustomElement` also works with Vue Single-File Components (SFCs). However, with the default tooling setup, the `