diff --git a/packages/runtime-core/src/helpers/renderSlot.ts b/packages/runtime-core/src/helpers/renderSlot.ts
index 92f7dab36..2f1296bb1 100644
--- a/packages/runtime-core/src/helpers/renderSlot.ts
+++ b/packages/runtime-core/src/helpers/renderSlot.ts
@@ -37,6 +37,7 @@ export function renderSlot(
isAsyncWrapper(currentRenderingInstance!.parent) &&
currentRenderingInstance!.parent.ce)
) {
+ const hasProps = Object.keys(props).length > 0
// in custom element mode, render as actual slot outlets
// wrap it with a fragment because in shadowRoot: false mode the slot
// element gets replaced by injected content
@@ -47,7 +48,7 @@ export function renderSlot(
Fragment,
null,
[createVNode('slot', props, fallback && fallback())],
- PatchFlags.STABLE_FRAGMENT,
+ hasProps ? PatchFlags.BAIL : PatchFlags.STABLE_FRAGMENT,
)
)
}
diff --git a/packages/runtime-dom/__tests__/customElement.spec.ts b/packages/runtime-dom/__tests__/customElement.spec.ts
index cb09cf4d9..52350dfd2 100644
--- a/packages/runtime-dom/__tests__/customElement.spec.ts
+++ b/packages/runtime-dom/__tests__/customElement.spec.ts
@@ -638,6 +638,33 @@ describe('defineCustomElement', () => {
`
`,
)
})
+
+ test('render slot props', async () => {
+ const foo = ref('foo')
+ const E = defineCustomElement({
+ render() {
+ return [
+ h(
+ 'div',
+ null,
+ renderSlot(this.$slots, 'default', { class: foo.value }),
+ ),
+ ]
+ },
+ })
+ customElements.define('my-el-slot-props', E)
+ container.innerHTML = `hi`
+ const e = container.childNodes[0] as VueElement
+ expect(e.shadowRoot!.innerHTML).toBe(
+ `
`,
+ )
+
+ foo.value = 'bar'
+ await nextTick()
+ expect(e.shadowRoot!.innerHTML).toBe(
+ `
`,
+ )
+ })
})
describe('provide/inject', () => {