<h1 align="center">vue-web-component-wrapper</h1>
<em>Transforming full-fledged Vue3 applications into reusable web components</em>
<img src="https://img.shields.io/badge/license-MIT-green" alt="License MIT">
<img src="https://img.shields.io/badge/version-1.7.7-blue" alt="version 1.7.7">
<img src="https://img.shields.io/badge/maintained-yes-brightgreen" alt="maintained yes">
vue-web-component-wrapper is a powerful Vue 3 plugin designed to transform full-fledged Vue applications into reusable web components (custom elements). These web components can be integrated into any website, enhancing flexibility and reusability.
As of now, Vue 3 does not support the creation of full applications as web components out of the box. This plugin aims to solve this problem by providing a simple and easy-to-use solution for creating web components from Vue applications. It also provides support for Vue ecosystem plugins such as Vuex, Pinia, Vue Router, Vue I18n, and VeeValidate.
Check out these demo projects to see vue-web-component-wrapper in action:
See the Documentation for more details.
v-model architecture.provide and inject.:root with :host: Optionally replace :root selectors with :host in your CSS to ensure styles are correctly scoped within the Shadow DOM.For more details, see the Documentation.
npm install vue-web-component-wrapper
# or
yarn add vue-web-component-wrapper
# or
pnpm add vue-web-component-wrapper
To create a web component using vue-web-component-wrapper, follow the steps below:
In your entry file, import the required modules:
import App from './App.vue';
import tailwindStyles from './assets/tailwind.css?raw';
import { createWebHashHistory, createRouter } from 'vue-router';
import { createI18n } from 'vue-i18n';
import { createStore } from 'vuex';
import { createPinia } from 'pinia';
import { defaultRoutes } from './main.routes.js';
import { store } from './store/index.js';
import {
defineCustomElement as VueDefineCustomElement,
h,
createApp,
getCurrentInstance,
} from 'vue';
import { createWebComponent } from 'vue-web-component-wrapper';
Configure your Vuex/Pinia store, Vue Router, and other Vue plugins:
export const pluginsWrapper = {
install(GivenVue) {
const Vue = GivenVue;
// Vuex
const createdStore = createStore(store);
Vue.use(createdStore);
// Or Pinia
const pinia = createPinia();
Vue.use(pinia);
// Vue Router
const router = createRouter({
history: createWebHashHistory(),
routes: defaultRoutes,
});
Vue.use(router);
// Vue I18n
const i18n = createI18n({
locale: 'en',
fallbackLocale: 'en',
});
Vue.use(i18n);
},
};
Use createWebComponent to create your web component. Specify your root Vue component, the element name, any plugins, and CSS framework styles:
createWebComponent({
rootComponent: App,
elementName: 'my-web-component',
plugins: pluginsWrapper,
cssFrameworkStyles: tailwindStyles,
VueDefineCustomElement,
h,
createApp,
getCurrentInstance,
disableStyleRemoval: false, // default is false
disableShadowDOM: false, // default is false
replaceRootWithHostInCssFramework: false, // default is false
loaderAttribute: 'data-web-component-loader', // default is 'data-web-component-loader'
hideSlotContentUntilMounted: true, // default is false
});
defineCustomElement function from Vue.h function from Vue.createApp function from Vue.getCurrentInstance function from Vue.:root selectors with :host in your CSS styles.data-web-component-loader).The asyncInitialization option accepts a function that returns a Promise. The custom element waits for this Promise to resolve before completing its initialization. This is useful for performing asynchronous tasks (e.g., API calls, dynamic imports) before the app mounts.
const asyncPromise = () => {
return new Promise((resolve) => {
setTimeout(() => {
resolve()
}, 1000)
})
}
createWebComponent({
rootComponent: App,
elementName: 'my-web-component',
plugins: pluginsWrapper,
cssFrameworkStyles: tailwindStyles,
VueDefineCustomElement,
h,
createApp,
getCurrentInstance,
asyncInitialization: asyncPromise, // default is Promise.resolve()
loaderAttribute: 'data-web-component-loader',
hideSlotContentUntilMounted: true, // default is false
});
The loaderAttribute option defines the attribute used to mark loader spinner elements in your custom element's DOM. Elements with this attribute will be removed automatically once the component is fully mounted.
<my-web-component
class="my-web-component"
>
</my-web-component>
<style>
.spinner {
border: 4px solid rgba(0, 0, 0, 0.1);
border-left-color: #4a90e2; /* Customize spinner color if needed */
border-radius: 50%;
width: 30px;
height: 30px;
animation: spin 1s linear infinite;
margin: auto;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
</style>
The hideSlotContentUntilMounted option hides the content of named slots until the component is fully mounted.
- By using the hidden attribute on the slot element, the content will be hidden until the component is fully mounted, and the web component wrapper will remove the hidden attribute once the component is fully mounted.
- This could be break the layout of your application, if you use the hidden attribute internally in your application.
- If you want to use the hidden attribute internally in your application, you can set the hideSlotContentUntilMounted option to false.
<my-web-component>
I am a custom named slot
</my-web-component>
The replaceRootWithHostInCssFramework option replaces all occurrences of :root with :host in your cssFrameworkStyles. This is useful when working with CSS variables defined on :root, ensuring they are properly scoped within the Shadow DOM.
createWebComponent({
rootComponent: App,
elementName: 'my-web-component',
plugins: pluginsWrapper,
cssFrameworkStyles: tailwindStyles,
VueDefineCustomElement,
h,
createApp,
getCurrentInstance,
replaceRootWithHost: true,
});
The cssFrameworkStyles option imports the CSS of your CSS framework or any other global CSS styles your application needs. By setting replaceRootWithHostInCssFramework to true, any :root selectors in your styles will be replaced with :host, ensuring correct scoping within the web component.
The nonce option is used to set a Content Security Policy (CSP) nonce for your web component. This is useful when your application uses inline scripts or styles, as it allows you to specify a unique nonce value that can be used to whitelist the inline content.
Tested bundlers to build the web-component application.
Vite Configuration
Here's a sample Vite configuration. Vite.js handles asset files like .css and .scss, and media files, importing them as usual. Vue files are parsed using the official @vitejs/plugin-vue.
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
export default defineConfig({
build: {
sourcemap: 'inline',
},
plugins: [
vue({
customElement: true,
}),
],
});
main.js/tsIn your main file, import the CSS framework with ?inline:
// Fonts are not loaded with ?inline; import font CSS in App.vue
import style from './style.css?inline';
App.vueWorkaround for fonts:
<style>
@import url('https://fonts.googleapis.com/css2?family=YourFont');
header {
@apply font-sans;
}
main {
@apply font-sans;
}
</style>
Webpack Configuration
Here's a sample webpack configuration to handle .vue, .css, and .scss files:
```javascript const path = require('path'); const { VueLoaderPlugin } = require('vue-loader'); const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = { mode: 'production', entry: './src/main.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'my-web-component.js', }, module: { rules: [ { test: /.(vue|ce.vue)$/, loader: 'vue-loader', options: { customElement: true, }, }, { test: /.(css|scss)$/, oneOf: [ { resourceQuery: /raw/, use: [ 'to-string-loader', 'css-loader', 'postcss-loader', { loader: 'sass-loader', options: { sassOptions: { indentedSyntax: false, }, }, }, ], }, { use: [ 'style-loader', 'css-loader', 'postcss-loader', { loader: 'sass-loader', options: { sassOptions: { indentedSyntax: false, }, }, }, ], }, ], }, { test: /.(png|jpe?g|gif|svg)(\?.*)?$/, loader: 'file-loader', options: { name: 'assets/[name].[hash:7].[ext]', },
$ claude mcp add vue-web-component-wrapper \
-- python -m otcore.mcp_server <graph>