When defined as a WebComponent, `useSlots()` and `$slots` return an empty object
Vue version
3.3.4
Link to minimal reproduction
https://stackblitz.com/edit/vitejs-vite-xycchc?file=src%2FApp.vue
Steps to reproduce
Just run and see the rendered content of useSlots()
and $slots
props:
What is expected?
Expected WebComponent to act similarly as vue component when accessing useSlots()
or $slots
.
What is actually happening?
useSlots()
and $slots
return an empty object when using as WebComponent.
System Info
System:
OS: Linux 5.10 Ubuntu 20.04 LTS (Focal Fossa)
CPU: (12) x64 12th Gen Intel(R) Core(TM) i7-1265U
Memory: 16.62 GB / 24.62 GB
Container: Yes
Shell: 5.0.17 - /bin/bash
Binaries:
Node: 18.12.1 - /usr/local/bin/node
npm: 8.19.2 - /usr/local/bin/npm
npmPackages:
vue: ^3.3.4 => 3.3.4
Any additional comments?
No response
Vue slots content comes in the form of functions that retrun vnodes. But when you wrapp a vue component as a custom element, this component will not receive vue slots content.
Instead, the custom element uses native <slot>
elements, and those inject actual HTML elements directly from the parent DOM. Vue doesn'T even see these elements, really. And I would not see it as a good idea to have $slots
suddently return HTML elements instead of vnode factory functions when used in a custom element.
However I do see an opportunity to improve the docs about this. The docs team would surely be thankful for an issue in their repo over at vuejs/docs
@LinusBorg thanks for respond.
I understand that there might be some differences and limitations when wrapping Vuejs component into a WebComponent. Although IMO every framework should strive to minimize those and make DX as fluent as possible.
By only updating docs, the problem won't be solved. Still Vuejs devs would have to struggle to come up with a workaround for such cases.
I would not see it as a good idea to have $slots suddently return HTML elements instead of vnode factory functions when used in a custom element.
When $slots
suddenly returning an empty object, that seems to be much worse idea, than returning at least a list of HTML Elements, wouldn't you agree?
Can you please consider implementing some workaround baked into Vuejs for better compatibility with WebComponents, and may be transforming this bug report into a feature request? 🙏
When $slots suddenly returning an empty object, that seems to be much worse idea, than returning at least a list of HTML Elements, wouldn't you agree?
I don't really agree. for one, the types would then need to reflect this, and developers would be forced to always take into account the possibilty of a slot suddenly conatining plain HTML elements instead of a vnode factory, even though they don't develop component to be used as web components.
If anything, a new API would be a better solution, or even just setting a template ref on a <slot />
(didn't try wether that already works right now with web component slots)
I was hoping ref on a <slot />
would work, but it doesn't, I have just checked.
Indeed a union return-types increases cognitive load and doesn't seem to be a good solution. And indeed apparently a new API is required. At the end of the day it still looks like a bug, and not like a feature or well reasoned limitation.
I ended up with this dirty workaround:
export function useSlot() {
const instance = getCurrentInstance()
const slot = ref<VNode[] | HTMLCollection | undefined>()
if (instance) {
onMounted(() => {
const root = instance.root.vnode.el
const isWebComponent = root?.parentNode instanceof ShadowRoot
if (!isWebComponent) {
const slots = useSlots()
slot.value = slots.default && slots.default()
} else if (root) {
const host = root.parentNode.host as HTMLElement
slot.value = host.children
}
})
}
return slot
}
If anyone has a better idea please reply in this issue.