{"id":20804387,"url":"https://github.com/EranGrin/vue-web-component-wrapper","last_synced_at":"2025-05-11T17:31:41.828Z","repository":{"id":165754373,"uuid":"641143683","full_name":"EranGrin/vue-web-component-wrapper","owner":"EranGrin","description":"vue3 - web component wrapper plugin","archived":false,"fork":false,"pushed_at":"2025-03-05T20:54:13.000Z","size":1205,"stargazers_count":77,"open_issues_count":1,"forks_count":10,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-04-07T07:12:34.109Z","etag":null,"topics":["custom-elements","vue3","web-component"],"latest_commit_sha":null,"homepage":"https://erangrin.github.io/vue-web-component-wrapper/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/EranGrin.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-05-15T21:49:21.000Z","updated_at":"2025-03-29T17:06:04.000Z","dependencies_parsed_at":"2024-01-04T14:26:51.249Z","dependency_job_id":"e3c0eaa3-e139-4970-9b12-8386b2259baa","html_url":"https://github.com/EranGrin/vue-web-component-wrapper","commit_stats":{"total_commits":102,"total_committers":7,"mean_commits":"14.571428571428571","dds":"0.13725490196078427","last_synced_commit":"3a43c6279aa9b6d301d6fc302c6a8d2739b0daba"},"previous_names":["erangrin/vue3-web-component-wrapper"],"tags_count":26,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/EranGrin%2Fvue-web-component-wrapper","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/EranGrin%2Fvue-web-component-wrapper/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/EranGrin%2Fvue-web-component-wrapper/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/EranGrin%2Fvue-web-component-wrapper/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/EranGrin","download_url":"https://codeload.github.com/EranGrin/vue-web-component-wrapper/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253604826,"owners_count":21934904,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["custom-elements","vue3","web-component"],"created_at":"2024-11-17T19:09:06.992Z","updated_at":"2025-05-11T17:31:41.817Z","avatar_url":"https://github.com/EranGrin.png","language":"TypeScript","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n    \u003ch1 align=\"center\"\u003evue-web-component-wrapper\u003c/h1\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n    \u003cem\u003eTransforming full-fledged Vue3 applications into reusable web components\u003c/em\u003e\n\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n\t\u003cimg src=\"https://img.shields.io/badge/license-MIT-green\" alt=\"License MIT\"\u003e\n\t\u003cimg src=\"https://img.shields.io/badge/version-1.7.6-blue\" alt=\"version 1.7.6\"\u003e\n\t\u003cimg src=\"https://img.shields.io/badge/maintained-yes-brightgreen\" alt=\"maintained yes\"\u003e\n\u003c/p\u003e\n\n\u003chr\u003e\n\n## Introduction\n\n**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.\n\n## Why Use vue-web-component-wrapper?\n\nAs 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](https://vuex.vuejs.org/), [Pinia](https://pinia.vuejs.org/), [Vue Router](https://router.vuejs.org/), [Vue I18n](https://vue-i18n.intlify.dev/), and [VeeValidate](https://vee-validate.logaretm.com/v4/).\n\n## Demo\n\nCheck out these demo projects to see **vue-web-component-wrapper** in action:\n\n- **Webpack Implementation**: [Webpack Demo Project](https://stackblitz.com/edit/vue-web-component-wrapper?file=README.md\u0026startScript=webpack-demo)\n- **Vite.js Implementation**: [Vite Demo Project](https://stackblitz.com/edit/vue-web-component-wrapper?file=README.md\u0026startScript=vite-demo)\n\n## Documentation\n\nSee the [Documentation](https://erangrin.github.io/vue-web-component-wrapper) for more details.\n\n## Key Features\n\n- **Vue Plugins Compatibility**: Seamlessly integrates with Vue ecosystem plugins like Vuex, Vue Router, and Vue I18n.\n- **CSS Framework Support**: Works with popular CSS frameworks such as Tailwind CSS, Bootstrap, Vuetify, Element Plus, and more.\n- **CSS Preprocessor Support**: Allows the use of CSS preprocessors like SCSS and LESS.\n- **Scoped CSS**: Supports scoped CSS in your components.\n- **Shadow DOM Support**: Encapsulates styles and scripts to prevent clashes with the rest of your application.\n- **Vue DevTools Support**: Compatible with the Vue DevTools browser extension.\n- **Slot and Named Slot Support**: Define and use slots and named slots within web components.\n- **v-model Support**: Improved support for two-way data binding using the `v-model` architecture.\n- **Event Emitting Support**: Emit and handle custom events from web components.\n- **Provide/Inject Support**: Pass data from parent to child components using `provide` and `inject`.\n- **Disable Removal of Styles on Unmount**: Control the removal of styles upon component unmount to solve issues with CSS transitions.\n- **Disable Shadow DOM**: Option to disable Shadow DOM for web components.\n- **Replace `:root` with `:host`**: Optionally replace `:root` selectors with `:host` in your CSS to ensure styles are correctly scoped within the Shadow DOM.\n- **Async Initialization**: Option to delay the initialization until its Promise resolves.\n- **Loader Support**: Support for loader spinner elements until the application is fully initialized.\n- **Hide slot content until the component is fully mounted**: Option to hide the content of named slots until the web-component is fully mounted.\n## CSS Frameworks Examples\n\n- **Tailwind CSS**: [Demo](https://stackblitz.com/edit/vue-web-component-wrapper?file=README.md\u0026startScript=tailwind-demo)\n- **UnoCSS**: [Demo](https://stackblitz.com/~/github.com/EranGrin/element-plus-unocss-web-component?file=src/style.css:L1-L2)\n- **Vuetify**: [Demo](https://stackblitz.com/~/github.com/EranGrin/vuetify-web-component-wrapper)\n- **Element Plus**: [Demo](https://stackblitz.com/~/github.com/EranGrin/element-plus-unocss-web-component?file=src/style.css:L1-L2)\n- **Bootstrap**: [Demo](https://stackblitz.com/~/github.com/EranGrin/bootstrap-demo-webcomponent)\n\nFor more details, see the [Documentation](https://erangrin.github.io/vue-web-component-wrapper).\n\n## Installation\n\n```bash\nnpm install vue-web-component-wrapper\n# or\nyarn add vue-web-component-wrapper\n# or\npnpm add vue-web-component-wrapper\n```\n\n## Usage\n\nTo create a web component using **vue-web-component-wrapper**, follow the steps below:\n\n### 1. Import the Necessary Modules\n\nIn your entry file, import the required modules:\n\n```javascript\nimport App from './App.vue';\nimport tailwindStyles from './assets/tailwind.css?raw';\nimport { createWebHashHistory, createRouter } from 'vue-router';\nimport { createI18n } from 'vue-i18n';\nimport { createStore } from 'vuex';\nimport { createPinia } from 'pinia';\nimport { defaultRoutes } from './main.routes.js';\nimport { store } from './store/index.js';\nimport {\n  defineCustomElement as VueDefineCustomElement,\n  h,\n  createApp,\n  getCurrentInstance,\n} from 'vue';\nimport { createWebComponent } from 'vue-web-component-wrapper';\n```\n\n### 2. Set Up the Instances and Plugins\n\nConfigure your Vuex/Pinia store, Vue Router, and other Vue plugins:\n\n```javascript\nexport const pluginsWrapper = {\n  install(GivenVue) {\n    const Vue = GivenVue;\n\n    // Vuex\n    const createdStore = createStore(store);\n    Vue.use(createdStore);\n\n    // Or Pinia\n    const pinia = createPinia();\n    Vue.use(pinia);\n\n    // Vue Router\n    const router = createRouter({\n      history: createWebHashHistory(),\n      routes: defaultRoutes,\n    });\n    Vue.use(router);\n\n    // Vue I18n\n    const i18n = createI18n({\n      locale: 'en',\n      fallbackLocale: 'en',\n    });\n    Vue.use(i18n);\n  },\n};\n```\n\n### 3. Create Your Web Component\n\nUse `createWebComponent` to create your web component. Specify your root Vue component, the element name, any plugins, and CSS framework styles:\n\n```javascript\ncreateWebComponent({\n  rootComponent: App,\n  elementName: 'my-web-component',\n  plugins: pluginsWrapper,\n  cssFrameworkStyles: tailwindStyles,\n  VueDefineCustomElement,\n  h,\n  createApp,\n  getCurrentInstance,\n  disableStyleRemoval: false, // default is false\n  disableShadowDOM: false,    // default is false\n  replaceRootWithHostInCssFramework: false, // default is false\n  loaderAttribute: 'data-web-component-loader', // default is 'data-web-component-loader'\n  hideSlotContentUntilMounted: true, // default is false\n});\n```\n\n#### Options Explained\n\n- **rootComponent**: The root component of your Vue application.\n- **elementName**: The tag name for your custom web component (must contain a hyphen and be lowercase).\n- **plugins**: Vue plugins to use in your application.\n- **cssFrameworkStyles**: Global CSS or SCSS styles your application needs.\n- **VueDefineCustomElement**: The `defineCustomElement` function from Vue.\n- **h**: The `h` function from Vue.\n- **createApp**: The `createApp` function from Vue.\n- **getCurrentInstance**: The `getCurrentInstance` function from Vue.\n- **disableStyleRemoval**: Disable removal of styles on unmount (useful for CSS transitions).\n- **disableShadowDOM**: Disable Shadow DOM for web components.\n- **replaceRootWithHostInCssFramework**: Replace `:root` selectors with `:host` in your CSS styles.\n- **asyncInitialization**: Accepts a function that returns a Promise.\n- **loaderAttribute**: Defines the attribute used to mark loader spinner (default is `data-web-component-loader`).\n- **hideSlotContentUntilMounted**: Hide the content of named slots until the component is fully mounted.\n\n### asyncInitialization\n\nThe `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.\n\n#### Example Usage\n\n```javascript\nconst asyncPromise = () =\u003e { \n  return new Promise((resolve) =\u003e {\n    setTimeout(() =\u003e {\n      resolve()\n    }, 1000)\n  })\n}\n\ncreateWebComponent({\n  rootComponent: App,\n  elementName: 'my-web-component',\n  plugins: pluginsWrapper,\n  cssFrameworkStyles: tailwindStyles,\n  VueDefineCustomElement,\n  h,\n  createApp,\n  getCurrentInstance,\n  asyncInitialization: asyncPromise, // default is Promise.resolve() \n  loaderAttribute: 'data-web-component-loader',\n  hideSlotContentUntilMounted: true, // default is false\n});\n```\n\n### loaderAttribute\n\nThe `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.\n\n```html\n    \u003cmy-web-component\n      class=\"my-web-component\"\n    \u003e\n      \u003c!-- named slot --\u003e\n      \u003cdiv class=\"customName\" data-web-component-loader slot=\"customName\"\u003e\n        \u003cdiv class=\"spinner\"\u003e\u003c/div\u003e\n      \u003c/div\u003e\n    \u003c/my-web-component\u003e\n\n  \u003cstyle\u003e\n    .spinner {\n    border: 4px solid rgba(0, 0, 0, 0.1);\n    border-left-color: #4a90e2; /* Customize spinner color if needed */\n    border-radius: 50%;\n    width: 30px;\n    height: 30px;\n    animation: spin 1s linear infinite;\n    margin: auto;\n  }\n\n  @keyframes spin {\n    to {\n      transform: rotate(360deg);\n    }\n  }\n  \u003c/style\u003e\n```\n\n### hideSlotContentUntilMounted\n\nThe `hideSlotContentUntilMounted` option hides the content of named slots until the component is fully mounted.\n- 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.\n- This could be break the layout of your application, if you use the `hidden` attribute internally in your application.\n- If you want to use the `hidden` attribute internally in your application, you can set the `hideSlotContentUntilMounted` option to `false`.\n\n```html\n\u003cmy-web-component\u003e\n  \u003c!-- named slot --\u003e\n  \u003cdiv class=\"customName\" hidden slot=\"customName\"\u003eI am a custom named slot \u003c/div\u003e\n\u003c/my-web-component\u003e\n```\n\n### replaceRootWithHostInCssFramework\n\nThe `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.\n\n\n#### Example Usage\n\n```javascript\ncreateWebComponent({\n  rootComponent: App,\n  elementName: 'my-web-component',\n  plugins: pluginsWrapper,\n  cssFrameworkStyles: tailwindStyles,\n  VueDefineCustomElement,\n  h,\n  createApp,\n  getCurrentInstance,\n  replaceRootWithHost: true,\n});\n```\n\n### cssFrameworkStyles\n\nThe `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.\n\n### 4. Build Your Application\n\nTested bundlers to build the web-component application.\n\n## Bundler Configurations\n\n\u003cdetails\u003e\n\u003csummary\u003eVite Configuration\u003c/summary\u003e\n\n### Vite.js Configuration\n\nHere'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](https://github.com/vitejs/vite-plugin-vue/tree/main/packages/plugin-vue).\n\n```javascript\nimport { defineConfig } from 'vite';\nimport vue from '@vitejs/plugin-vue';\n\nexport default defineConfig({\n  build: {\n    sourcemap: 'inline',\n  },\n  plugins: [\n    vue({\n      customElement: true,\n    }),\n  ],\n});\n```\n\n#### `main.js/ts`\n\nIn your main file, import the CSS framework with `?inline`:\n\n```javascript\n// Fonts are not loaded with ?inline; import font CSS in App.vue\nimport style from './style.css?inline';\n```\n\n#### `App.vue`\n\nWorkaround for fonts:\n\n```html\n\u003cstyle\u003e\n@import url('https://fonts.googleapis.com/css2?family=YourFont');\n\nheader {\n  @apply font-sans;\n}\n\nmain {\n  @apply font-sans;\n}\n\u003c/style\u003e\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eWebpack Configuration\u003c/summary\u003e\n\n### Webpack Configuration\n\nHere's a sample webpack configuration to handle `.vue`, `.css`, and `.scss` files:\n\n```javascript\nconst path = require('path');\nconst { VueLoaderPlugin } = require('vue-loader');\nconst HtmlWebpackPlugin = require('html-webpack-plugin');\n\nmodule.exports = {\n  mode: 'production',\n  entry: './src/main.js',\n  output: {\n    path: path.resolve(__dirname, 'dist'),\n    filename: 'my-web-component.js',\n  },\n  module: {\n    rules: [\n      {\n        test: /\\.(vue|ce\\.vue)$/,\n        loader: 'vue-loader',\n        options: {\n          customElement: true,\n        },\n      },\n      {\n        test: /\\.(css|scss)$/,\n        oneOf: [\n          {\n            resourceQuery: /raw/,\n            use: [\n              'to-string-loader',\n              'css-loader',\n              'postcss-loader',\n              {\n                loader: 'sass-loader',\n                options: {\n                  sassOptions: {\n                    indentedSyntax: false,\n                  },\n                },\n              },\n            ],\n          },\n          {\n            use: [\n              'style-loader',\n              'css-loader',\n              'postcss-loader',\n              {\n                loader: 'sass-loader',\n                options: {\n                  sassOptions: {\n                    indentedSyntax: false,\n                  },\n                },\n              },\n            ],\n          },\n        ],\n      },\n      {\n        test: /\\.(png|jpe?g|gif|svg)(\\?.*)?$/,\n        loader: 'file-loader',\n        options: {\n          name: 'assets/[name].[hash:7].[ext]',\n        },\n      },\n    ],\n  },\n  plugins: [\n    new VueLoaderPlugin(),\n    new HtmlWebpackPlugin({\n      template: './public/index.html',\n    }),\n  ],\n  resolve: {\n    alias: {\n      vue$: 'vue/dist/vue.esm-bundler.js',\n    },\n    extensions: ['.js', '.vue', '.json'],\n  },\n};\n```\n\n#### `main.js/ts`\n\nImport the CSS framework with `?raw`:\n\n```javascript\nimport style from './style.css?raw';\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eVite + Rollup Configuration\u003c/summary\u003e\n\n### Vite + Rollup Configuration\n\nThis configuration provides enhanced build options using Vite with Rollup:\n\n```typescript\nimport { defineConfig, UserConfig } from 'vite';\nimport vue from '@vitejs/plugin-vue';\n\nexport default defineConfig(({ mode }): UserConfig =\u003e {\n  return {\n    esbuild: {\n      // Remove debugger statements in production\n      drop: mode === 'production' ? ['debugger'] : [],\n    },\n    build: {\n      emptyOutDir: true,\n      target: 'ES2020',\n      rollupOptions: {\n        output: {\n          // Maintain original file names\n          entryFileNames: '[name].js',\n        },\n      },\n      // Disable CSS code splitting\n      cssCodeSplit: false,\n    },\n    plugins: [\n      vue({\n        template: {\n          compilerOptions: {\n            // Define custom elements starting with 'app-element'\n            isCustomElement: (tag) =\u003e tag.startsWith('app-element'),\n          },\n        },\n        customElement: true,\n      }),\n      {\n        // Hot reload fix for Vue components\n        name: 'force-reload',\n        handleHotUpdate({ file, server }) {\n          if (file.endsWith('.vue')) {\n            server.ws.send({ type: 'full-reload' });\n            return [];\n          }\n        },\n      },\n    ],\n  };\n});\n```\n\n**Features:**\n\n\n- Custom element support for tags starting with 'app-element'.\n- Disabled CSS code splitting for better web component compatibility.\n- Hot reload improvements for Vue components.\n- Rollup output configuration to maintain file names.\n\n\u003c/details\u003e\n\n## Web Component Without Shadow DOM\n\nTo create a web component without Shadow DOM, set the `disableShadowDOM` option to `true` in the `createWebComponent` function:\n\n```javascript\ncreateWebComponent({\n  // ...other options\n  disableShadowDOM: true,\n});\n```\n\nThis 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.\n\n### Demo Without Shadow DOM\n\n[Demo Link](https://stackblitz.com/~/github.com/EranGrin/web-component-no-shadow-dom-demo)\n\n## SFC as Custom Element\n\nEnhance the functionality of Single File Components (SFC) as Custom Elements using `defineCustomElement` with two new features:\n\n1. **Nested Components**: Use nested components with styles, sharing base components between multiple custom elements.\n2. **Shadow DOM Option**: Disable Shadow DOM for the SFC custom element.\n\n### Usage\n\n```javascript\n// main.js\nimport { defineCustomElementSFC } from 'vue-web-component-wrapper';\nconst MyComponentElement = defineCustomElementSFC(MyComponent, { shadowRoot: false });\ncustomElements.define('my-component', MyComponentElement);\n```\n\n### Demo SFC Custom Element\n\n[Demo Link](https://stackblitz.com/edit/vue-web-component-wrapper?file=README.md\u0026startScript=SFC-demo)\n\n## Tips\n\n- **Testing Production Build**: To test your production build, run a local server in the `dist` folder. You can use [Valet](https://laravel.com/docs/10.x/valet) or any local server.\n\n## Future Plans\n\n1. **TypeScript Support**: Adding proper strict types.\n\n## Contributing\n\nContributions are welcome! To contribute:\n\n- **Fork** the repository.\n- **Create a new branch** for your feature or bug fix.\n- **Make your changes** and commit them with a clear message.\n- **Push your changes** to your fork.\n- **Submit a pull request** to the main repository.\n\nPlease follow the code style and conventions used in the project.\n\nIf you find a bug or have a feature request, please [open an issue](https://github.com/EranGrin/vue-web-component-wrapper/issues).\n\n## License\n\nThis project is licensed under the MIT License.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FEranGrin%2Fvue-web-component-wrapper","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FEranGrin%2Fvue-web-component-wrapper","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FEranGrin%2Fvue-web-component-wrapper/lists"}