-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Progress] nuxt@3
support
#433
Comments
@nuxt/bridge
and nuxt@3
support
@nuxt/bridge
and nuxt@3
supportnuxt@3
support
do you have any idea when the nuxt 3 version will be available ? |
Excuse me, when will nuxt-custom-elements support nuxt3? Is there a tutorial? |
+1 |
I created an issue (nuxt/nuxt#15584) to request native support by nuxt |
Hello All (@maximepvrt @pperzyna ) On the Very fresh, so still experimental.
Basically the state already produces an expected result. |
Hey, Thank you very much for all the hard work.
Any help or pointers would be greatly appreciated. |
Hello @Techbinator, can try again, for whatever reason Now the release is generated with the project dependencies. |
works great now. Thanks a lot for the quick fix |
impossible to run the nuxt application with default builder (
nuxt 3.4.2 |
Hello @maximepvrt,
Import via entry definition |
@maximepvrt The generated files are now also in the public path with a build or generate. https://www.npmjs.com/package/nuxt-custom-elements/v/2.0.0-beta.12 |
@maximepvrt Did you take this over? Is a placeholder from the IDE, if so best remove again. Alternatively you could share the |
Hey @ThornWalli, I'm a bit stuck and hoping you could help. Everything seems to be working except for styles not being included with the web component. I'm not sure if this is a bug or if I'm missing something with my setup. Styles are missing in two instances
For a bit of background we're not necessarily building a full scale app with Nuxt and then using certain components as web components. More so using Nuxt as an opinionated framework to build Vue based web components that will be used in a legacy non-Vue website. I say this as I saw your comment of
and was wondering what setting this would be. As it could be the potential solution to my issue. My project file snippets are below. Thank you in advance 🙏
{
"name": "nuxt-app",
"private": true,
"scripts": {
"build": "nuxt build",
"dev": "nuxt dev",
"generate": "nuxt generate",
"preview": "nuxt preview",
"postinstall": "nuxt prepare"
},
"devDependencies": {
"@nuxtjs/tailwindcss": "^6.7.0",
"@types/node": "^18",
"nuxt": "^3.5.0"
},
"dependencies": {
"@pinia/nuxt": "^0.4.10",
"nuxt-custom-elements": "^2.0.0-beta.12",
"pinia": "^2.0.36"
},
"overrides": {
"vue": "latest"
}
}
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
ssr: false,
target: 'static',
devServer: {
port: 4321,
},
modules: [
'@nuxtjs/tailwindcss',
'@pinia/nuxt',
'nuxt-custom-elements',
],
customElements: {
entries: [
{
name: 'Example',
tags: [
{
name: 'CustomElementExample',
path: '@/components/Example',
options: {
props: {
exampleTitle: 'Nuxt Config Prop Title',
},
},
slotContent: 'Hello from the Nuxt Config!',
},
],
},
],
},
});
<script lang="ts" setup>
export interface Props {
exampleTitle?: string
}
withDefaults(defineProps<Props>(), {
exampleTitle: 'Default example title',
});
console.log('hello world 00');
onBeforeMount(() => {
console.log('hello world 01');
});
</script>
<template>
<div class="text-white bg-zinc-700 flex flex-col p-4 rounded-lg">
<div class="italic">
{{ exampleTitle }}
</div>
<div class="text-zinc-200">
<slot>Default Content</slot>
</div>
</div>
</template>
<script lang="ts" setup>
const nuxtApp = useNuxtApp();
onBeforeMount(() => {
nuxtApp.$customElements.registerEntry('example');
});
</script>
<template>
<div>
<client-only>
<div class="grid grid-cols-3 gap-6 p-6">
<Example example-title="Vue Component">
Vue component slot content
</Example>
<custom-element-example example-title="Web component title">
Web component slot content
</custom-element-example>
</div>
</client-only>
</div>
</template> |
Hello @zackspear, Case 1
Case 2 You need to import It must always be remembered that the entries are standalone vue component builds that do not take anything from nuxt. And please consider this case: vuejs/core#4662 If a style tag is included in the entry, it will also be included in the generate. https://github.com/GrabarzUndPartner/nuxt-custom-elements/blob/main/example/components/Example.vue |
@ThornWalli thank you so much! 🙏 I have a working example web component with styles. I changed Then for <style lang="postcss">
@tailwind base;
@tailwind components;
@tailwind utilities;
</style> Then within Really appreciate the tips. I knew I was close. Here's my full files for those that may find this later.
export default defineNuxtConfig({
ssr: false,
target: 'static',
devServer: {
port: 4321,
},
modules: [
'@nuxtjs/tailwindcss',
'@pinia/nuxt',
'nuxt-custom-elements',
],
customElements: {
entries: [
{
name: 'Example',
tags: [
{
name: 'CustomElementExample',
path: '@/components/Example.ce',
options: {
props: {
exampleTitle: 'Nuxt Config Prop Title',
},
},
slotContent: 'Hello from the Nuxt Config!',
},
],
},
],
},
});
<script lang="ts" setup>
import 'tailwindcss/tailwind.css';
export interface Props {
exampleTitle?: string
}
withDefaults(defineProps<Props>(), {
exampleTitle: 'Default example title',
});
console.log('hello world 00');
onBeforeMount(() => {
console.log('hello world 01');
});
</script>
<template>
<div class="text-white bg-zinc-700 flex flex-col p-4 rounded-lg">
<h1 class="italic">
{{ exampleTitle }}
</h1>
<div class="text-zinc-200">
<slot>Default Content</slot>
</div>
</div>
</template>
<style lang="postcss">
@tailwind base;
@tailwind components;
@tailwind utilities;
</style>
<script lang="ts" setup>
const nuxtApp = useNuxtApp();
onBeforeMount(() => {
nuxtApp.$customElements.registerEntry('example');
});
</script>
<template>
<div class="bg-gray-200">
<client-only>
<div class="grid grid-cols-3 gap-6 p-6">
<ExampleCe example-title="Vue Component">
Vue component slot content
</ExampleCe>
<custom-element-example example-title="Web component title">
Web component slot content
</custom-element-example>
</div>
</client-only>
</div>
</template> |
@zackspear Very good 🙂 I consider times whether one does not need the It is important that this issue about child style is clarified. As long as it is not really usable for more complex projects. |
@ThornWalli unfortunately having a different, unrelated issue now. Not sure if it's a bug or something that I'm doing wrong. I created another component which is intended to be a web component. But in both development and in the generated Nuxt output the second web component is rendering as the first web component. I made my second test component, And a screenshot of
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
ssr: false,
target: 'static',
devServer: {
port: 4321,
},
modules: [
'@vueuse/nuxt',
'@pinia/nuxt',
'@nuxtjs/tailwindcss',
'nuxt-custom-elements',
],
customElements: {
entries: [
{
name: 'ConnectComponents',
tags: [
{
name: 'ConnectExample',
path: '@/components/Example.ce',
options: {
props: {
heading: 'Example Nuxt Config Prop Title',
},
},
slotContent: 'Hello Example from the Nuxt Config!',
},
{
name: 'ConnectTester',
path: '@/components/Tester.ce',
options: {
props: {
copy: 'Tester copy from Nuxt config',
},
},
},
],
},
],
},
});
<script lang="ts" setup>
import 'tailwindcss/tailwind.css';
export interface Props {
heading?: string
}
withDefaults(defineProps<Props>(), {
heading: 'Default example heading',
});
const { x, y } = useMouse();
</script>
<template>
<div class="text-white bg-zinc-700 flex flex-col p-4 rounded-lg">
<h1 class="italic">
{{ heading }}
</h1>
<p>Mouse coordinates: {{ x }}, {{ y }}</p>
<div class="text-zinc-200">
<slot>Default example content</slot>
</div>
</div>
</template>
<style lang="postcss">
@tailwind base;
@tailwind components;
@tailwind utilities;
</style>
<script lang="ts" setup>
import 'tailwindcss/tailwind.css';
export interface Props {
copy?: string
}
withDefaults(defineProps<Props>(), {
copy: 'Default tester copy',
});
const { x, y } = useMouse();
</script>
<template>
<div class="text-white bg-red-700 flex flex-col p-4 rounded-lg">
<h1 class="italic">Tester component</h1>
<h2>Mouse coordinates: {{ x }}, {{ y }}</h2>
<p class="text-gray-300">
{{ copy }}
</p>
</div>
</template>
<style lang="postcss">
@tailwind base;
@tailwind components;
@tailwind utilities;
</style>
<script lang="ts" setup>
const nuxtApp = useNuxtApp();
onBeforeMount(() => {
nuxtApp.$customElements.registerEntry('ConnectComponents');
});
</script>
<template>
<div class="max-w-3xl mx-auto bg-gray-200">
<client-only>
<div class="grid grid-cols-2 gap-6 p-6">
<ExampleCe heading="Example Vue Component">
Example Vue component slot content
</ExampleCe>
<connect-example heading="Example web component title">
Example web component slot content
</connect-example>
<TesterCe heading="Tester Vue Component" />
<connect-tester copy="Tester web component title"></connect-tester>
</div>
</client-only>
</div>
</template> Now if I switch the order of the tag objects in // https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
ssr: false,
target: 'static',
devServer: {
port: 4321,
},
modules: [
'@vueuse/nuxt',
'@pinia/nuxt',
'@nuxtjs/tailwindcss',
'nuxt-custom-elements',
],
customElements: {
entries: [
{
name: 'ConnectComponents',
tags: [
{
name: 'ConnectTester',
path: '@/components/Tester.ce',
options: {
props: {
copy: 'Tester copy from Nuxt config',
},
},
},
{
name: 'ConnectExample',
path: '@/components/Example.ce',
options: {
props: {
heading: 'Example Nuxt Config Prop Title',
},
},
slotContent: 'Hello Example from the Nuxt Config!',
},
],
},
],
},
}); Thanks again in advance. Edit: It looks like I was looking through import { defineAsyncComponent, defineCustomElement } from 'vue'
import Component0 from '@/components/Example.ce';
import Component1 from '@/components/Tester.ce';
const defineTags = () => {
const elements = [
['connect-example', (typeof Component0 === 'function' ? (new Component0).$options : Component0)],
['connect-tester', (typeof Component0 === 'function' ? (new Component0).$options : Component0)]
].forEach(([name, component]) => {
const CustomElement = defineCustomElement(component);
window.customElements.define(name, CustomElement);
})
};
const setup = () => {
defineTags();
};
setup();
Edit2: I created a separate issue for this #521 and was able to figure out the fix in #522 |
@kahl-dev Thanks good to know 😉 |
@ThornWalli if set the option export default defineNuxtConfig({
customElements: {
entries: [
{
name: 'XXX',
shadow: false,
//...
}
]
}
}) |
@kahl-dev Unfortunately, the behaviour we knew from In There is also a reason why there is this package |
Hello @ThornWalli , I hope you are doing well. I am using your product and encountered an issue, and you are my last hope!) So, the problem is that in our Nuxt application that we are developing, we need to implement web components for the possibility of integrating them into external sites, and for this, I want to use your solution. It's worth noting that the components are implemented using Vuetify, and this is where the issues arise. I followed all the documentation provided at this link link and this example However, after running the "nuxt build" command and launching the built web component, I'm not getting styles from Vuetify components; I only receive the layout components and styles that do not belong to the Vuetify library. When I try to create a simple component using plain HTML and CSS, everything works properly. Please advise if it's possible to solve this problem so that Vuetify library elements are displayed in the web component with styles. Below, I will provide some screenshots for better understanding. Thank you in advance for your work and time!
import { resolve } from 'path'
import type { NuxtConfig } from '@nuxt/types'
const config: NuxtConfig = {
ssr: false,
server: {
hmr: {
protocol: 'ws',
host: 'localhost'
}
},
runtimeConfig: {
public: {
DNS_SENTRY: process.env.DNS_SENTRY,
CURRENT_ENV: process.env.CURRENT_ENV,
GALENE_DOMAIN: process.env.GALENE_DOMAIN
}
},
buildModules: ['@nuxt/typescript-build', '@nuxtjs/vuetify'],
devtools: process.env.DEVTOOLS === 'true' ? { enabled: true } : false,
modules: ['nuxt-lodash', '@pinia/nuxt', 'nuxt-custom-elements'],
build: {
transpile: ['vuetify', 'trpc-nuxt']
},
layouts: {
adminLayout: '@/layouts/AdminLayout.vue',
createLessonLayout: '@/layouts/createLesson.vue'
},
css: ['@/assets/styles/main.scss'],
app: {
head: {
charset: 'utf-8',
viewport: 'width=device-width, initial-scale=1, minimum-scale=1'
}
},
customElements: {
analyzer: false,
entries: [
{
name: 'web-component',
tags: [
{
async: true,
name: 'LessonList',
path: '@/custom-elements/lessonList.ce',
}
]
}
]
},
vite: {
server: {
cors: {
preflightContinue: true
}
},
vue: {
customElement: true
}
},
routeRules: {
'*': { cors: true },
'_nuxt/*': { cors: true }
}
}
export default config
import '@mdi/font/css/materialdesignicons.css'
import { createVuetify } from 'vuetify'
import * as components from 'vuetify/components'
import * as directives from 'vuetify/directives'
import 'vuetify/styles'
const componentsList = { ...components }
export default defineNuxtPlugin((nuxtApp) => {
const vuetify = createVuetify({
ssr: true,
components: componentsList,
directives
})
nuxtApp.vueApp.use(vuetify)
})
<template>
<Suspense>
<ChooseLesson />
</Suspense>
</template>
<script setup lang="ts">
import ChooseLesson from '../components/chooseLesson/ChooseLesson.vue'
</script>
<style>
@import 'vuetify/dist/vuetify.min.css';
</style>
<style lang="scss">
@import '@/assets/styles/main.scss';
@import '@/assets/styles/LessonList.scss';
</style>
"devDependencies": {
"@mdi/font": "^7.2.96",
"@nuxt/devtools": "latest",
"@nuxt/types": "^2.17.0",
"@nuxt/typescript-build": "^3.0.1",
"@nuxtjs/eslint-config-typescript": "^12.0.0",
"@nuxtjs/style-resources": "^1.2.1",
"@types/bcryptjs": "^2.4.2",
"@types/jsonwebtoken": "^9.0.2",
"@types/node": "^18.17.1",
"@types/nodemailer": "^6.4.9",
"@typescript-eslint/eslint-plugin": "^5.61.0",
"@typescript-eslint/parser": "^5.61.0",
"eslint": "^8.44.0",
"eslint-config-prettier": "^8.10.0",
"eslint-plugin-nuxt": "^4.0.0",
"eslint-plugin-prettier": "^4.2.1",
"nuxt": "^3.8.2",
"prettier": "^2.8.8",
"prisma": "^5.1.1",
"sass": "^1.63.6",
"ts-node": "^10.9.1",
"typescript": "^5.1.6",
"vue-i18n": "^9.2.2"
},
"dependencies": {
"@aws-sdk/client-s3": "^3.391.0",
"@fullcalendar/core": "^6.1.9",
"@fullcalendar/daygrid": "^6.1.9",
"@fullcalendar/interaction": "^6.1.9",
"@fullcalendar/timegrid": "^6.1.9",
"@fullcalendar/vue": "^6.1.9",
"@fullcalendar/vue3": "^6.1.9",
"@nuxtjs/vuetify": "^1.12.3",
"@pinia/nuxt": "^0.4.11",
"@prisma/client": "^5.1.1",
"@sentry/browser": "^7.63.0",
"@sentry/node": "^7.63.0",
"@sentry/tracing": "^7.63.0",
"@sentry/vite-plugin": "^2.6.2",
"@sentry/vue": "^7.63.0",
"@trpc/client": "^10.35.0",
"@trpc/server": "^10.35.0",
"bcryptjs": "^2.4.3",
"get-youtube-id": "^1.0.1",
"joi": "^17.9.2",
"jsonwebtoken": "^9.0.1",
"nodemailer": "^6.9.4",
"nuxt-custom-elements": "2.0.0-beta.16",
"nuxt-lodash": "^2.5.0",
"pinia": "^2.1.6",
"quill": "^1.3.7",
"quill-placeholder-module": "^0.3.1",
"sharp": "^0.32.6",
"trpc-nuxt": "^0.10.6",
"uniqid": "^5.4.0",
"vue-lite-youtube-embed": "^1.2.0",
"vuedraggable": "^4.1.0",
"vuetify": "3.4.0",
"zod": "^3.22.3"
}, |
Hello @Stereoboi, I also noticed this some time ago... I was wondering how Here the theming is injected via the app, I suspect that the position is not handled in the Workaround would be to import a finished Vuetify Theme CSS, in the component. There are discussions about this on Vuetify: |
Hey @ThornWalli, Ran into something odd here. Looks like I'm getting double style tags in the project – one's popping up at the root and the other's where it's supposed to be. It's messing with the styling and I can't figure out why it's happening. Have you seen this kind of thing before? Any idea why there'd be a duplicate tag at the root? If you've got some tips or know something I might be missing, that'd be awesome. Here is the component and the result:
|
Hello @kahl-dev, I suspect a bug in this plugin https://github.com/unplugin/unplugin-vue-ce/tree/master/packages/sub-style Nested styles are not officially supported in Vue3, so a workaround was created as a plugin by the community. A small Vite-only project with this plugin for reproduction would be good to report this error directly to the plugin. |
Hey @ThornWalli, Thanks for the quick response! I'm going to put together a demo that replicates this issue and report the bug to the plugin devs. Really appreciate your help in pinpointing the possible cause. |
if someone's trying to extend vite builder config to make output files without hashes:
|
@dselivanovvv Do you have the config in the right place? 🙂 Your configuration works! {
customElements: {
analyzer: !isTest,
entries: [
{
name: 'Example',
viteExtend(config) {
config.build.rollupOptions = config.build.rollupOptions || {};
config.build.rollupOptions.output =
config.build.rollupOptions.output || {};
const target = config.build.rollupOptions.output;
const assetsDir = 'assets';
target.entryFileNames = `${assetsDir}/[name].js`;
target.chunkFileNames = `${assetsDir}/[name].js`;
target.assetFileNames = `${assetsDir}/[name].[ext]`;
return config;
},
tags: [
{
async: false,
name: 'CustomElementExample',
path: '@/components/customElements/Example.vue',
options: {
props: {
title: 'Live Example'
}
},
appContext: '@/components/customElements/Example.appContext.js',
slotContent: '<div>Live Example Content</div>'
}
]
}
]
}
} |
Hello. I've been trying out this project for a while now, and so far I feel like I'm just banging my head on a brick wall. I'm trying to export some very rudimentary nuxt 3 components using the composition API. I've been able to generate a custom element that creates a shadow dom and displays the component. However, none of the nuxt-specific features seem to work in these components, or at least no auto-imports seem to be working. So I'm having to import things like computed or the components that I'm trying to use. So this means that none of my actual components will work, since they naturally use these things. Is this supposed to be the case or am I doing something wrong? |
Hello @MarkoTukiainen Macros are not supported 🙃 Try to import the things. e.g. All global functions are announced under |
Thanks for replying so quickly. So, in order to use my existing nuxt components I'd have to modify all of them to use manual imports? |
Yes, that's right. |
Trying the
Which, I suppose, is correct since the file |
Yes, indeed that was my original question - I was wondering if I in fact had to refactor all my components to use manual imports. Interestingly enough, the #imports syntax works fine in nuxt, but just not when building custom elements. Could this be a Windows-specific issue, if the path resolution of the #imports alias somehow goes wrong or something? I'll have to see how big of an effort this will be, sadly the auto-imports was one of the best things I thought Vue 3 brought to the table. |
Auto imports are good as long as you stay in Nuxt ;) Example: Mono repo with component library for reuse in different projects (without Nuxt). That's where it gets a bit difficult. You could extend the Vite configuration for the auto-imports. Here you would have to see if a pure alias The custom elements are each created in Vite/Webpack with their own entry builds, so Nuxt-specific properties are missing here. |
I'm beginning to see that. I've been working my way through some of these components and I'm having a really bad time trying to reuse any of the nuxt code in both a nuxt application and in these widgets. Plugins are not available, so for example an instance of pinia seems to be very difficult to get a hold of. All the documentation online assumes that I'm 100% going to use nuxt features, so alternate solutions are hard to find and even more difficult to figure out by myself. I came up with this for a store plugin:
I figured I could call getStore() to get an instance of the store when the plugin is not available, and use $store elsewhere. While this works when SSR rendering the nuxt app, another plugin (that is run later than this plugin) runs into the Error I'm throwing on the client side. Do you have a recommended best practice for solving something like this? The examples seem to be all vue 2. |
I had to rethink the custom element a little.
You can find something about Pinia here:
And this is used as a base: Note that the official Vue 3 custom element support is a bit difficult, starts with styles and ends with the integration of Vue plugins... So keep that in mind as well 😉 |
Thanks for taking the time to link those examples, this looks very helpful. I was actually able to work around my issue with plugins and getting a handle on a single instance of pinia earlier today, and was able to implement a widget that reused the same code that still simultaneously works in a separate nuxt application through layers. But that |
Hey @ThornWalli, I've pretty much completed the conversion of my widgets from vue 2 to vue 3 (using my nuxt 3 components). I've had some issues autoloading components (though most other autoloading works fine). But I've just now noticed a potentially catastrophic issue. Vite+rollup seem to create an es module, which requires a script module tag to run. This is not really feasible for us, since our customers already have these tags in use everywhere, so we really can't change the tag. Previously we created an UMD bundle with Webpack which split the code into chunks. However, Rollup doesn't seem to want to do this. So I end up with an absolutely massive, single entry .js file for the widget. Do you have any recommendations on how to get around this? E.g. output a UMD bundle with chunks. These build tools are really not my forte. |
Unfortunately no solution for But you could make a nuxt.config.js {
builder: '@nuxt/webpack-builder'
} |
I suppose that's an option. For now I ended up with renaming the new module and just calling import from the old script, seems to work fine. In case someone else is reading this thread, here's a couple of things I noticed made reusing the code much better (for me):
|
I encountered an issue when actually using it. The version I am using is package.json:
nuxt.config.ts:
components\Example.vue:
plugins\customElement.ts:
pages\example.vue:
all code: |
Hello @wsm661, did I understand correctly, you want to integrate the CustomElement correctly during development? The basic idea of this module is actually to create exports of components as an app for use on external pages, as a widget or something. When you generate the project, a separate build is started. Locally you could try this with Alternatively, you could think about integrating it in the development, currently the components are imported directly. Without |
Thanks. Your interpretation is accurate; I aim to integrate CustomElement into my project. Within the project, I aim to display the as-is, allowing the browser to render it, rather than treating it as a Vue component and prematurely rendering it into an HTML structure. We operate under a singular project, and the current project requires the registration of Web Components for direct utilization. I attempted using I grasp your sentiment, hence I reimplemented the contents of |
Hello @wsm661, I created a PR to see what the implementation could look like. The composable now refers directly to the entry. Unfortunately, I ran into limitations here. CustomElement is missing the style.
The workaround would be to add .ce.vue to all files that are built into the CustomElement. |
neither of these two configs worked for me to remove the hash |
Nuxt 3 version is available 🎉
Install
Todos
vue-web-component-wrapper
for usagei18n
,pinia
,vuetify
unplugin-vue-ce
webpack
Build (@nuxt/webpack-builder
)build
orgenerate
.publicPath
integration? (https://webpack.js.org/guides/public-path/#automatic-publicpath)webpack
configvite
Build (@nuxt/vite-builder
)build
orgenerate
.publicPath
integration? (https://www.npmjs.com/package/vite-plugin-dynamic-base)vite
configvitest
Tests update@nuxt/vite-builder
@nuxt/webpack-builder
nuxt
backward compatibility (nuxt-bridge
)Features
Problems?
The text was updated successfully, but these errors were encountered: