diff --git a/packages/compiler-core/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap b/packages/compiler-core/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap
index 2cd13bab0..e93d74fe2 100644
--- a/packages/compiler-core/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap
+++ b/packages/compiler-core/__tests__/transforms/__snapshots__/vSlot.spec.ts.snap
@@ -1,5 +1,31 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
+exports[`compiler: transform component slots > Properly handle v-for and ref on template 1`] = `
+"const _Vue = Vue
+
+return function render(_ctx, _cache) {
+ with (_ctx) {
+ const { toDisplayString: _toDisplayString, createElementVNode: _createElementVNode, resolveComponent: _resolveComponent, withCtx: _withCtx, renderList: _renderList, createSlots: _createSlots, openBlock: _openBlock, createBlock: _createBlock } = _Vue
+
+ const _component_Comp = _resolveComponent("Comp")
+
+ return (_openBlock(), _createBlock(_component_Comp, null, _createSlots({ _: 2 /* DYNAMIC */ }, [
+ _renderList(_ctx.list, (name) => {
+ return {
+ name: name,
+ fn: _withCtx(() => [
+ _createElementVNode("p", {
+ ref_for: true,
+ ref: "slotItems"
+ }, _toDisplayString(name), 513 /* TEXT, NEED_PATCH */)
+ ])
+ }
+ })
+ ]), 1024 /* DYNAMIC_SLOTS */))
+ }
+}"
+`;
+
exports[`compiler: transform component slots > dynamically named slots 1`] = `
"const { toDisplayString: _toDisplayString, resolveComponent: _resolveComponent, withCtx: _withCtx, openBlock: _openBlock, createBlock: _createBlock } = Vue
diff --git a/packages/compiler-core/__tests__/transforms/vSlot.spec.ts b/packages/compiler-core/__tests__/transforms/vSlot.spec.ts
index e0f44a064..aeef8f0e5 100644
--- a/packages/compiler-core/__tests__/transforms/vSlot.spec.ts
+++ b/packages/compiler-core/__tests__/transforms/vSlot.spec.ts
@@ -371,6 +371,39 @@ describe('compiler: transform component slots', () => {
expect(generate(root, { prefixIdentifiers: true }).code).toMatchSnapshot()
})
+ test('Properly handle v-for and ref on template', () => {
+ const { root, slots } = parseWithSlots(
+ `
+ {{name}}
+ `,
+ { prefixIdentifiers: true },
+ )
+
+ const spy = {
+ type: NodeTypes.JS_PROPERTY,
+ key: {
+ constType: 3,
+ type: NodeTypes.SIMPLE_EXPRESSION,
+ content: 'ref_for',
+ isStatic: true,
+ },
+ value: {
+ constType: 0,
+ type: NodeTypes.SIMPLE_EXPRESSION,
+ content: 'true',
+ isStatic: false,
+ },
+ }
+
+ const properties = (slots as any).arguments[1].elements[0].arguments[1]
+ .returns.properties[1].value.returns[0].codegenNode.props.properties[0]
+ expect(properties).toMatchObject(spy)
+ expect((root as any).children[0].codegenNode.patchFlag).toMatch(
+ PatchFlags.DYNAMIC_SLOTS + '',
+ )
+ expect(generate(root).code).toMatchSnapshot()
+ })
+
test('nested slots scoping', () => {
const { root, slots } = parseWithSlots(
`
diff --git a/packages/compiler-core/src/transforms/vSlot.ts b/packages/compiler-core/src/transforms/vSlot.ts
index 43296dcc9..f24b99d89 100644
--- a/packages/compiler-core/src/transforms/vSlot.ts
+++ b/packages/compiler-core/src/transforms/vSlot.ts
@@ -9,6 +9,7 @@ import {
NodeTypes,
type ObjectExpression,
type Property,
+ type RenderSlotCall,
type SlotsExpression,
type SourceLocation,
type TemplateChildNode,
@@ -25,7 +26,9 @@ import { ErrorCodes, createCompilerError } from '../errors'
import {
assert,
findDir,
+ findProp,
hasScopeRef,
+ injectProp,
isStaticExp,
isTemplateNode,
isVSlot,
@@ -258,6 +261,8 @@ export function buildSlots(
const parseResult = vFor.forParseResult
if (parseResult) {
finalizeForParseResult(parseResult, context)
+ // #8395
+ injectRefFor(slotElement.children, context)
// Render the dynamic slots as an array and add it to the createSlot()
// args. The runtime knows how to handle it appropriately.
dynamicSlots.push(
@@ -419,3 +424,19 @@ function isNonWhitespaceContent(node: TemplateChildNode): boolean {
? !!node.content.trim()
: isNonWhitespaceContent(node.content)
}
+
+function injectRefFor(
+ children: TemplateChildNode[],
+ context: TransformContext,
+) {
+ for (let i = 0; i < children.length; i++) {
+ const child = children[i]
+ if (child.type === NodeTypes.ELEMENT && findProp(child, 'ref')) {
+ const Property = createObjectProperty(
+ createSimpleExpression('ref_for', true),
+ createSimpleExpression('true'),
+ )
+ injectProp(child.codegenNode as RenderSlotCall, Property, context)
+ }
+ }
+}