core icon indicating copy to clipboard operation
core copied to clipboard

fix(ssr): filter out transition-specific props to avoid invalid HTML attributes

Open ZKunZhang opened this issue 3 months ago • 5 comments

Summary:

  • Fixed TransitionGroup passing transition-related props to DOM elements incorrectly
  • Added prop filtering to exclude transition-specific and component-specific attributes (tag, moveClass)
  • Resolved W3C validation errors with invalid HTML attributes

Changes: Filtered props before passing to createVNode, excluding transition props and tag/moveClass while retaining valid HTML attributes.

const filteredProps: Record<string, any> = {}
for (const key in rawProps) {
  if (!(key in TransitionPropsValidators) && key !== 'tag' && key !== 'moveClass') {
    filteredProps[key] = (rawProps as any)[key]
  }
}
return createVNode(tag, filteredProps, children)

Fixes: #13037, W3C validation errors. No breaking changes.

Summary by CodeRabbit

  • Bug Fixes

    • SSR now omits transition-related props from rendered HTML for transition-group (supports both kebab- and camel-case, including move-class), and ensures consistent attribute output for static, dynamic, and mixed v-bind scenarios with an attrs fallback when needed.
  • Tests

    • Added comprehensive tests covering transition prop filtering and the updated SSR attribute rendering behavior.

ZKunZhang avatar Sep 15 '25 14:09 ZKunZhang

Walkthrough

Reintroduces TransitionGroup prop filtering on the SSR side by updating the SSR compiler transform to mark transition contexts and pass an isTransition flag to ssrRenderAttrs, and by extending the server renderer's ssrRenderAttrs to skip transition-related props (kebab and camel forms) when isTransition is true. Tests updated/added accordingly.

Changes

Cohort / File(s) Summary
SSR Compiler Transform: TransitionGroup
packages/compiler-ssr/src/transforms/ssrTransformTransitionGroup.ts
Reconstructs runtime-like Transition props validators, adds kebabToCamel, uses hasOwn to detect attributes, filters transition-related attributes (including moveClass/move-class) in phase 1, stores tag/propsExp in WIP for phase 2, and emits _ssrRenderAttrs(..., tagOrExpr, true) when appropriate.
Server Renderer: Attributes Helper
packages/server-renderer/src/helpers/ssrRenderAttrs.ts
Extends ssrRenderAttrs signature to (props, tag?, isTransition?); adds transitionPropsToFilter and kebabToCamel logic to skip transition-specific keys (both camelCase and kebab-case) when isTransition is true.
Tests: Compiler SSR TransitionGroup
packages/compiler-ssr/__tests__/ssrTransitionGroup.spec.ts
Updates inline snapshots to reflect _ssrRenderAttrs(..., tagOrExpr, true) usage and adds tests covering transition-prop filtering, moveClass handling, static/dynamic tags, v-bind scenarios, event handler omission, and mixed bindings.
Tests: Server Renderer Attributes
packages/server-renderer/__tests__/ssrRenderAttrs.spec.ts
Adds tests asserting transition-prop filtering when isTransition is true and preserving all props when isTransition is false/undefined.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Template as SFC Template
  participant Compiler as compiler-ssr (ssrTransformTransitionGroup)
  participant Renderer as server-renderer (ssrRenderAttrs)
  participant Output as SSR Output

  Template->>Compiler: compile <TransitionGroup> (props, tag)
  Note over Compiler: Phase 1 - collect tag, filter attrs (kebab & camel), set isTransition
  Compiler->>Compiler: store WIP (tag, propsExp)
  Compiler->>Renderer: call _ssrRenderAttrs(_attrs, tagOrExpr, true)
  Note over Renderer: if isTransition=true -> skip Transition props (camel+kebab) & moveClass
  Renderer-->>Compiler: filtered attributes string
  Compiler-->>Output: emit HTML fragment with filtered attributes

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested labels

:hammer: p3-minor-bug, scope:hydration

Poem

I nibbled at tags in the moonlit glen,
Camel and kebab I chased down again.
I plucked stray props from the SSR feast,
Left lists tidy and markup at peace.
Hoppy renderings—clean and keen. 🐇✨

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "fix(ssr): filter out transition-specific props to avoid invalid HTML attributes" concisely and accurately summarizes the main change—filtering transition-related props from SSR output to prevent invalid HTML attributes—and is specific enough for history scanning.
Linked Issues Check ✅ Passed The changes address issue [#13037] by filtering transition-specific props in both the SSR compiler output and the server-renderer (compiler-ssr now excludes transition props and ssrRenderAttrs gains an isTransition filter), and added tests cover name, moveClass, and v-bind scenarios so the SSR output no longer emits transition props as DOM attributes.
Out of Scope Changes Check ✅ Passed All modifications are confined to SSR compilation and runtime attribute rendering (packages/compiler-ssr, packages/server-renderer) and related tests; no unrelated modules were changed, although ssrRenderAttrs gained an optional isTransition parameter which is updated in call sites as part of this feature.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • [ ] 📝 Generate Docstrings
🧪 Generate unit tests
  • [ ] Create PR with unit tests
  • [ ] Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

coderabbitai[bot] avatar Sep 15 '25 14:09 coderabbitai[bot]

I don't think this change has fixed the problem.

Here's a Playground using this PR. The name attribute is still present in the generated HTML:

As far as I'm aware, the original issue only occurs when SSR is enabled. The extra props are already removed during client-side rendering.

Perhaps I'm missing something. Are you able to provide a Playground (using the Netlify preview of this PR), or even better include some test cases that demonstrate the fix is working correctly?

Sorry, this does affect SSR after all. I had a misunderstanding of the code, and I'll fix this part again.

ZKunZhang avatar Sep 17 '25 00:09 ZKunZhang

I don't think this change has fixed the problem.

Here's a Playground using this PR. The name attribute is still present in the generated HTML:

As far as I'm aware, the original issue only occurs when SSR is enabled. The extra props are already removed during client-side rendering.

Perhaps I'm missing something. Are you able to provide a Playground (using the Netlify preview of this PR), or even better include some test cases that demonstrate the fix is working correctly?

During my local testing, I noticed that the actual effect is inconsistent with the display in the Vue SFC Playground. Currently, I’m not quite clear on how to present local effects through the Playground. However, I have integrated the build artifacts from Playground into the project for server-side rendering, and no issues occurred during this process.

What confuses me now is: since the same files are used, why are there discrepancies in the final build results?
P.S. The repository address where I used the aforementioned build artifacts is deploy-preview-13894-ssr-test.git image

ZKunZhang avatar Sep 17 '25 11:09 ZKunZhang

The SSR example you linked appears to be using a render function, so it won't match what happens in the Playground.

For components that use templates, Vue compiles a separate SSR version that bypasses VNodes and just generates the HTML string. You can see that version in the Playground by clicking on the SSR tab.

Playground

It would appear that this is where the spurious name attribute is being added to the output.

skirtles-code avatar Sep 17 '25 20:09 skirtles-code

Also need to consider the usage of v-bind="prop". see Playground with this PR

edison1105 avatar Sep 23 '25 06:09 edison1105