Vue Web Component Wrapper
vue-web-component-wrapper is a Vue 3 plugin that facilitates the creation of reusable web components (custom elements) from full-fledged Vue applications. The current stable version is 1.7.7, with frequent patch and minor releases addressing bug fixes and introducing new features. This library differentiates itself by providing comprehensive support for the broader Vue ecosystem, including seamless integration with state management (Vuex, Pinia), routing (Vue Router), internationalization (Vue I18n), and validation (VeeValidate). It also offers robust compatibility with popular CSS frameworks like Tailwind CSS, Bootstrap, and Vuetify, alongside support for CSS preprocessors and scoped styles. Key features include Shadow DOM encapsulation, Vue DevTools integration, full slot and `v-model` support, event emitting, `provide`/`inject`, and options for fine-grained control over Shadow DOM behavior and CSS styling (e.g., `:root` to `:host` replacement). It also supports async initialization and loader mechanisms.
Common errors
-
Warning: __NO_SIDE_EFFECTS__ after building with vite
cause During the Vite build process, earlier versions of `vue-web-component-wrapper` could trigger warnings related to `__NO_SIDE_EFFECTS__` due to how the package's module side effects were interpreted.fixUpgrade to `vue-web-component-wrapper@1.6.9` or newer, which includes a specific fix addressing this warning when building with Vite. -
TypeScript errors when importing package (e.g., 'Cannot find module 'vue-web-component-wrapper' or missing type definitions for exports')
cause Earlier versions of the package had incomplete or incorrect TypeScript type definitions, leading to compilation errors or warnings in TypeScript projects when importing or using its exports.fixUpgrade to `vue-web-component-wrapper@1.6.5` or newer to resolve known TypeScript definition issues and ensure proper type inference and compilation. -
Slot content not rendering correctly or unexpectedly disappearing when `disableShadowDOM: true`.
cause A bug existed in earlier versions that specifically affected the processing of slot content, especially with nested elements, when the `disableShadowDOM` option was enabled, leading to incorrect rendering.fixUpgrade to `vue-web-component-wrapper@1.6.11` or newer. This version includes a fix that addresses issues with slot processing and nested elements when Shadow DOM is disabled.
Warnings
- gotcha Careful consideration of CSS scoping is required when utilizing or opting to disable Shadow DOM. By default, styles are encapsulated within Shadow DOM, preventing them from affecting the global document. However, if `disableShadowDOM` is set to `true`, component styles may bleed into the global document. Additionally, the `replaceCssRootToHost` option is crucial for correctly scoping `:root` selectors within Shadow DOM, as `:root` otherwise targets the document root.
- gotcha Prior to version 1.7.7, integrating web components created with this wrapper into environments with strict Content Security Policies (CSPs) might have required manual adjustments or led to issues with script and style execution due to the injection of inline styles and scripts. Version 1.7.7 introduced `nonce`/CSP handling to mitigate these problems.
- gotcha While `vue-web-component-wrapper` includes support for Vue DevTools, debugging components encapsulated within a Shadow DOM can be more challenging than in a standard Vue application. Browser developer tools often require specific settings to inspect Shadow DOM content effectively.
Install
-
npm install vue-web-component-wrapper -
yarn add vue-web-component-wrapper -
pnpm add vue-web-component-wrapper
Imports
- wrap
const wrap = require('vue-web-component-wrapper');import { wrap } from 'vue-web-component-wrapper'; - WebComponentWrapperOptions
import type { WebComponentWrapperOptions } from 'vue-web-component-wrapper'; - defineComponent, createApp
import { defineComponent, createApp } from 'vue';
Quickstart
import { createApp, defineComponent } from 'vue';
import { wrap } from 'vue-web-component-wrapper';
const MyVueComponent = defineComponent({
props: {
message: String,
count: { type: Number, default: 0 }
},
emits: ['increment'],
data() {
return {
internalCount: this.count
};
},
methods: {
increment() {
this.internalCount++;
this.$emit('increment', this.internalCount);
}
},
template: `
<div style="padding: 1rem; border: 1px solid #1a73e8; border-radius: 4px; margin-bottom: 1rem;">
<h3>Hello from Vue Web Component!</h3>
<p>Prop Message: <strong>{{ message }}</strong></p>
<p>Current Count: <strong>{{ internalCount }}</strong></p>
<button @click="increment" style="padding: 8px 16px; background-color: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer;">Increment Count</button>
<slot name="footer"></slot>
</div>
`,
});
const app = createApp(MyVueComponent);
const WebComponent = wrap(app, MyVueComponent, {
props: {
message: { type: String, default: 'Default Message' },
count: { type: Number, default: 0 }
},
emits: ['increment'],
// Example of wrapper options
disableShadowDOM: false, // Keep Shadow DOM for encapsulation
replaceCssRootToHost: true // Essential for :root styles within Shadow DOM
});
// Register the custom element in the browser's CustomElementRegistry
customElements.define('my-vue-counter', WebComponent);
// Example HTML usage:
// <my-vue-counter message="Initial Web Component" count="5"></my-vue-counter>
// <my-vue-counter message="Another instance with slot content">
// <p slot="footer" style="color: gray; font-size: 0.9em;">This is content for the footer slot.</p>
// </my-vue-counter>