I would like to build a web component using Vuetify3 inside of a Vue3's component that is using composition API.
How do I add Vuetify3 to MyNavbar.ce.vue
component so that it is scoped inside the component's shadow root?
This component will be dynamically loaded into applications/sites built with other frameworks/styles.
I want the component to be able to use Vuetify3 directives such v-app
, v-app-bar
, v-btn
etc. within it.
Thank you, Oleg
I followed many online resources and arrived at the setup below but I can't get vuetify setup inside the component.
Project created:
npm create vue@latest
> npx
> create-vue
✔ Project name: … my-navbar
✔ Add TypeScript? … No
✔ Add JSX Support? … No
✔ Add Vue Router for Single Page Application development? … No
✔ Add Pinia for state management? … No
✔ Add Vitest for Unit Testing? … No
✔ Add an End-to-End Testing Solution? › No
✔ Add ESLint for code quality? › No
Scaffolding project in /path/my-navbar...
Done. Now run:
cd my-navbar
npm install
npm run dev
Vuetify installation:
cd my-navbar
npm install @mdi/font
npm i vuetify
Current files
src/main.js
import { defineCustomElement } from 'vue'
import MyNavbar from './components/MyNavbar.ce.vue'
// convert into custom element constructor
const element = defineCustomElement(MyNavbar)
// register
customElements.define('my-navbar', element)
index.html
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Custom Element</title>
</head>
<body>
<my-navbar></my-navbar>
<script type="module" src="/src/main.js"></script>
</body>
</html>
./src/components/MyNavbar.ce.vue
<script setup>
</script>
<template>
<v-app>
<v-app-bar>
<template v-slot:prepend>
<v-app-bar-nav-icon></v-app-bar-nav-icon>
</template>
<v-app-bar-title>Application Bar</v-app-bar-title>
</v-app-bar>
</v-app>
</template>
<style scoped>
@import 'vuetify/styles';
</style>>
I would like to build a web component using Vuetify3 inside of a Vue3's component that is using composition API.
How do I add Vuetify3 to MyNavbar.ce.vue
component so that it is scoped inside the component's shadow root?
This component will be dynamically loaded into applications/sites built with other frameworks/styles.
I want the component to be able to use Vuetify3 directives such v-app
, v-app-bar
, v-btn
etc. within it.
Thank you, Oleg
I followed many online resources and arrived at the setup below but I can't get vuetify setup inside the component.
Project created:
npm create vue@latest
> npx
> create-vue
✔ Project name: … my-navbar
✔ Add TypeScript? … No
✔ Add JSX Support? … No
✔ Add Vue Router for Single Page Application development? … No
✔ Add Pinia for state management? … No
✔ Add Vitest for Unit Testing? … No
✔ Add an End-to-End Testing Solution? › No
✔ Add ESLint for code quality? › No
Scaffolding project in /path/my-navbar...
Done. Now run:
cd my-navbar
npm install
npm run dev
Vuetify installation:
cd my-navbar
npm install @mdi/font
npm i vuetify
Current files
src/main.js
import { defineCustomElement } from 'vue'
import MyNavbar from './components/MyNavbar.ce.vue'
// convert into custom element constructor
const element = defineCustomElement(MyNavbar)
// register
customElements.define('my-navbar', element)
index.html
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Custom Element</title>
</head>
<body>
<my-navbar></my-navbar>
<script type="module" src="/src/main.js"></script>
</body>
</html>
./src/components/MyNavbar.ce.vue
<script setup>
</script>
<template>
<v-app>
<v-app-bar>
<template v-slot:prepend>
<v-app-bar-nav-icon></v-app-bar-nav-icon>
</template>
<v-app-bar-title>Application Bar</v-app-bar-title>
</v-app-bar>
</v-app>
</template>
<style scoped>
@import 'vuetify/styles';
</style>>
Share
Improve this question
asked Mar 6 at 8:41
OlegOleg
12 bronze badges
6
- Why are you importing the Vuetify CSS into a single scoped element locally? Why not into your global CSS? – rozsazoltan Commented Mar 6 at 8:45
- Did you registered Vuetify in the app (see docs)? – Moritz Ringler Commented Mar 6 at 9:25
- Heya, thanks for the prompt response. @rozsazoltan re:css I'm not sure if this is the correct approach but my idea to import it inside the component is to ensure the css will be scoped to the web component alone so when I import this component the will be no collision. – Oleg Commented Mar 6 at 14:57
- Heya @moritz-ringler thanks for the prompt response. I don't have an app only a single component this is why I'm asking the question. Because in a "standard" spa I know how to initialize vuetify and all works fine but in this case I'm not sure. – Oleg Commented Mar 6 at 15:00
- 1 Right, have a look at stackoverflow/a/76934503/4883195 – Moritz Ringler Commented Mar 6 at 15:13
1 Answer
Reset to default 0Thanks to @moritz-ringler for bringing up stackoverflow/a/76934503/4883195. I've managed to put together a working github/OnlyLoveOleg/vue3-vuetify-webcomponent example in case anyone is looking for a solution to this problem.
index.html
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vue3 Vuetify Web Component</title>
</head>
<body>
<my-navbar></my-navbar>
<script type="module" src="/src/main.js"></script>
</body>
</html>
src/main.js
import { defineCustomElement } from './defineCustomElementWithStyles'
import '@mdi/font/css/materialdesignicons.css'
import { createVuetify } from 'vuetify';
import { aliases, mdi } from 'vuetify/iconsets/mdi'
// Import the Vue component.
import MyNavbarComponent from './components/MyNavbar.ce.vue'
const vuetify = createVuetify({
icons: {
defaultSet: 'mdi',
aliases,
sets: {
mdi,
},
},
})
customElements.define(
'my-navbar',
defineCustomElement(MyNavbarComponent, {
plugins: [vuetify],
})
)
src/defineCustomElementWithStyles.js
// defineCustomElementWithStyles.js
import { defineCustomElement as VueDefineCustomElement, h, createApp, getCurrentInstance } from 'vue'
export const defineCustomElement = (component, { plugins = [] } = {}) =>
VueDefineCustomElement({
styles: component.styles,
render: () => h(component),
setup() {
const app = createApp()
// install plugins
plugins.forEach(app.use)
const inst = getCurrentInstance()
Object.assign(inst.appContext, app._context)
Object.assign(inst.provides, app._context.provides)
},
})
src/components/MyNavbar.ce.vue
<template>
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr/npm/vuetify@3/dist/vuetify.min.css" />
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr/npm/@mdi/[email protected]/css/materialdesignicons.min.css" />
<v-app>
<v-app-bar>
<template v-slot:prepend>
<v-app-bar-nav-icon></v-app-bar-nav-icon>
</template>
<v-app-bar-title>App title</v-app-bar-title>
<v-spacer></v-spacer>
<v-btn icon>
<v-icon>mdi-magnify</v-icon>
</v-btn>
<v-btn icon>
<v-icon>mdi-heart</v-icon>
</v-btn>
<v-btn icon>
<v-icon>mdi-dots-vertical</v-icon>
</v-btn>
</v-app-bar>
</v-app>
</template>