Vue Shadow DOM Integration
vue-shadow-dom is a JavaScript library providing robust Shadow DOM support for Vue 3 applications, currently stable at version 4.2.0. It allows developers to leverage the browser's native Shadow DOM capabilities directly within Vue components, enabling strict encapsulation of styles and markup. The library achieves this through the provision of a Vue plugin, a `v-shadow` directive, and dedicated `<shadow-root>` and `<shadow-style>` components. While there isn't a stated fixed release cadence, updates tend to align with major Vue ecosystem changes or feature enhancements. A key differentiator is its direct, declarative API for Shadow DOM usage, avoiding complex manual DOM manipulations, and its comprehensive support for various module environments including ESM, CommonJS, and UMD builds, catering to both bundler-driven and browser-only setups. Notably, versions 2.0 and above are specifically designed for Vue 3, while v1.x targets Vue 2.
Common errors
-
Failed to resolve component: shadow-root
cause The `vue-shadow-dom` plugin has not been correctly registered with the Vue application instance, meaning its components and directives are not globally available.fixAfter creating your Vue application with `createApp()`, ensure you register the plugin by calling `app.use(shadow)` before mounting the application, e.g., `const app = createApp(MyRootComponent); app.use(shadow); app.mount('#app');` -
TypeError: app.use is not a function
cause This error typically indicates that `app` is not a valid Vue 3 application instance. This can happen if you are using Vue 2's `new Vue()` API instead of Vue 3's `createApp()` or if `app` is otherwise undefined.fixConfirm that your application entry point uses `createApp()` to initialize your Vue 3 application. If you are targeting Vue 2, you must use `vue-shadow-dom@1` and its corresponding initialization method for Vue 2 plugins.
Warnings
- breaking Version 2.0 and higher of `vue-shadow-dom` are exclusively built for Vue 3. Attempting to use these versions with Vue 2 applications will result in runtime errors due to significant API changes.
- gotcha Using the `abstract` prop on `<shadow-root>` attaches the shadow root directly to the host element. This makes other external sibling HTML tags within the same host element unavailable or unrenderable inside the Shadow DOM, which can lead to unexpected layout or missing content.
- gotcha Importing the incorrect distribution file (e.g., a bundler-specific ESM build directly in a browser without a module loader, or a UMD build in a modern bundler setup) can cause module resolution failures or runtime errors.
Install
-
npm install vue-shadow-dom -
yarn add vue-shadow-dom -
pnpm add vue-shadow-dom
Imports
- shadow
import { shadow } from 'vue-shadow-dom'import shadow from 'vue-shadow-dom'
- ShadowRootExpose
import type { ShadowRootExpose } from 'vue-shadow-dom'
Quickstart
import { createApp, ref, defineComponent } from 'vue';
import shadow, { type ShadowRootExpose } from 'vue-shadow-dom';
const MyShadowComponent = defineComponent({
template: `
<div style="border: 1px solid blue; padding: 10px; margin: 20px;">
<h2>Component Host</h2>
<p>Content outside the shadow root. Styles here will leak unless encapsulated.</p>
<shadow-root ref="shadowRef" tag="section" abstract>
<template #default>
<shadow-style>
p { color: green; font-weight: bold; }
h3 { color: darkred; }
button { background-color: lightblue; padding: 8px; border: none; cursor: pointer; }
</shadow-style>
<h3>Shadow Root Content</h3>
<p>This paragraph should be green and bold inside the shadow DOM.</p>
<button @click="logShadowRoot">Log Shadow Root</button>
</template>
</shadow-root>
<p>This paragraph is outside the shadow root.</p>
</div>
`,
setup() {
const shadowRef = ref<ShadowRootExpose | null>(null);
const logShadowRoot = () => {
if (shadowRef.value && shadowRef.value.shadow_root) {
console.log('Shadow Root Instance:', shadowRef.value.shadow_root);
// You can interact with the native ShadowRoot object directly
const pElement = shadowRef.value.shadow_root.querySelector('p');
if (pElement) {
console.log('Paragraph inside shadow root:', pElement.textContent);
}
} else {
console.warn('ShadowRoot instance not available yet.');
}
};
return { shadowRef, logShadowRoot };
},
});
const app = createApp(MyShadowComponent);
app.use(shadow);
app.mount('#app');
// To run this, you would typically have an index.html like:
// <div id="app"></div>