
Security News
Open Source Maintainers Feeling the Weight of the EU’s Cyber Resilience Act
The EU Cyber Resilience Act is prompting compliance requests that open source maintainers may not be obligated or equipped to handle.
vue-web-component-wrapper
Advanced tools
A Vue 3 plugin that provides a web component wrapper with styles, seamlessly integrating with Vuex, Vue Router, Vue I18n, and supporting Tailwind CSS and Sass styles.
Transforming full-fledged Vue3 applications into reusable web components
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"
>
<!-- named slot -->
<div class="customName" data-web-component-loader slot="customName">
<div class="spinner"></div>
</div>
</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.
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.hidden
attribute internally in your application.hidden
attribute internally in your application, you can set the hideSlotContentUntilMounted
option to false
.<my-web-component>
<!-- named slot -->
<div class="customName" hidden slot="customName">I am a custom named slot </div>
</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.
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/ts
In 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.vue
Workaround for fonts:
<style>
@import url('https://fonts.googleapis.com/css2?family=YourFont');
header {
@apply font-sans;
}
main {
@apply font-sans;
}
</style>
Here's a sample webpack configuration to handle .vue
, .css
, and .scss
files:
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]',
},
},
],
},
plugins: [
new VueLoaderPlugin(),
new HtmlWebpackPlugin({
template: './public/index.html',
}),
],
resolve: {
alias: {
vue$: 'vue/dist/vue.esm-bundler.js',
},
extensions: ['.js', '.vue', '.json'],
},
};
main.js/ts
Import the CSS framework with ?raw
:
import style from './style.css?raw';
This configuration provides enhanced build options using Vite with Rollup:
import { defineConfig, UserConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
export default defineConfig(({ mode }): UserConfig => {
return {
esbuild: {
// Remove debugger statements in production
drop: mode === 'production' ? ['debugger'] : [],
},
build: {
emptyOutDir: true,
target: 'ES2020',
rollupOptions: {
output: {
// Maintain original file names
entryFileNames: '[name].js',
},
},
// Disable CSS code splitting
cssCodeSplit: false,
},
plugins: [
vue({
template: {
compilerOptions: {
// Define custom elements starting with 'app-element'
isCustomElement: (tag) => tag.startsWith('app-element'),
},
},
customElement: true,
}),
{
// Hot reload fix for Vue components
name: 'force-reload',
handleHotUpdate({ file, server }) {
if (file.endsWith('.vue')) {
server.ws.send({ type: 'full-reload' });
return [];
}
},
},
],
};
});
Features:
To create a web component without Shadow DOM, set the disableShadowDOM
option to true
in the createWebComponent
function:
createWebComponent({
// ...other options
disableShadowDOM: true,
});
This feature uses a patch to the Vue source code, which may lead to issues with future versions of Vue. Please report any issues in the repository.
Enhance the functionality of Single File Components (SFC) as Custom Elements using defineCustomElement
with two new features:
// main.js
import { defineCustomElementSFC } from 'vue-web-component-wrapper';
const MyComponentElement = defineCustomElementSFC(MyComponent, { shadowRoot: false });
customElements.define('my-component', MyComponentElement);
dist
folder. You can use Valet or any local server.Contributions are welcome! To contribute:
Please follow the code style and conventions used in the project.
If you find a bug or have a feature request, please open an issue.
This project is licensed under the MIT License.
FAQs
A Vue 3 plugin that provides a web component wrapper with styles, seamlessly integrating with Vuex, Vue Router, Vue I18n, and supporting Tailwind CSS and Sass styles.
The npm package vue-web-component-wrapper receives a total of 3,147 weekly downloads. As such, vue-web-component-wrapper popularity was classified as popular.
We found that vue-web-component-wrapper demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
The EU Cyber Resilience Act is prompting compliance requests that open source maintainers may not be obligated or equipped to handle.
Security News
Crates.io adds Trusted Publishing support, enabling secure GitHub Actions-based crate releases without long-lived API tokens.
Research
/Security News
Undocumented protestware found in 28 npm packages disrupts UI for Russian-language users visiting Russian and Belarusian domains.