Skip to content

Conversation

@Gianthard-cyh
Copy link

@Gianthard-cyh Gianthard-cyh commented Dec 30, 2025

close #14262.

Summary by CodeRabbit

  • Bug Fixes

    • Server-side rendering now respects render-time attribute modifiers: bindings prefixed with ^ are emitted as attributes in SSR output, while bindings prefixed with . are ignored to prevent incorrect attribute emission.
  • Tests

    • Added a test that verifies render-time attribute modifiers are processed and appear correctly in server-rendered HTML.

✏️ Tip: You can customize this high-level summary in your review settings.

Copilot AI review requested due to automatic review settings December 30, 2025 15:43
@coderabbitai
Copy link

coderabbitai bot commented Dec 30, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Walkthrough

Handles render-time prop modifiers during SSR: strips a leading ^ to force attribute rendering and ignores keys starting with . (prop-only) so they are not emitted as attributes. Adds a test validating modifier syntax in render functions.

Changes

Cohort / File(s) Summary
Server-side Attribute Rendering
packages/server-renderer/src/helpers/ssrRenderAttrs.ts
Converts loop key to mutable let, skips keys beginning with . (prop-only), normalizes keys starting with ^ by stripping the caret so they render as attributes, and preserves existing textarea/value skipping.
SSR Render Tests
packages/server-renderer/__tests__/render.spec.ts
Adds test "props modifiers in render attrs" to assert that ^ (attr) and . (prop) modifiers in render-time props produce expected SSR HTML output.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested labels

:hammer: p3-minor-bug

Suggested reviewers

  • skirtles-code

Poem

🐰 I nibbled carets, nudged the dot away,
So SSR's tidy tags could safely stay.
A tiny trim, a hop, a cheer—
Markup neat, and render clear. 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: fixing SSR handling of v-bind modifiers during render attribute processing.
Linked Issues check ✅ Passed The code changes directly address issue #14262 by implementing logic to skip property-prefixed keys (.) and normalize attribute-prefixed keys (^) in ssrRenderAttrs, with test coverage validating the modifier behavior.
Out of Scope Changes check ✅ Passed All changes are scoped to fixing SSR handling of render-function prop modifiers: test case validates modifier syntax, and ssrRenderAttrs implementation handles the modifier logic as required.
✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 43a462e and 0120fd2.

📒 Files selected for processing (1)
  • packages/server-renderer/__tests__/render.spec.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/server-renderer/tests/render.spec.ts

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.

@github-actions
Copy link

Size Report

Bundles

File Size Gzip Brotli
runtime-dom.global.prod.js 103 kB 39.1 kB 35.2 kB
vue.global.prod.js 161 kB 59.1 kB 52.6 kB

Usages

Name Size Gzip Brotli
createApp (CAPI only) 47 kB 18.4 kB 16.8 kB
createApp 55.2 kB 21.4 kB 19.6 kB
createSSRApp 59.4 kB 23.2 kB 21.1 kB
defineCustomElement 60.7 kB 23.1 kB 21.1 kB
overall 69.5 kB 26.7 kB 24.3 kB

@pkg-pr-new
Copy link

pkg-pr-new bot commented Dec 30, 2025

Open in StackBlitz

@vue/compiler-core

pnpm add https://pkg.pr.new/@vue/compiler-core@14263
npm i https://pkg.pr.new/@vue/compiler-core@14263
yarn add https://pkg.pr.new/@vue/[email protected]

@vue/compiler-dom

pnpm add https://pkg.pr.new/@vue/compiler-dom@14263
npm i https://pkg.pr.new/@vue/compiler-dom@14263
yarn add https://pkg.pr.new/@vue/[email protected]

@vue/compiler-sfc

pnpm add https://pkg.pr.new/@vue/compiler-sfc@14263
npm i https://pkg.pr.new/@vue/compiler-sfc@14263
yarn add https://pkg.pr.new/@vue/[email protected]

@vue/compiler-ssr

pnpm add https://pkg.pr.new/@vue/compiler-ssr@14263
npm i https://pkg.pr.new/@vue/compiler-ssr@14263
yarn add https://pkg.pr.new/@vue/[email protected]

@vue/reactivity

pnpm add https://pkg.pr.new/@vue/reactivity@14263
npm i https://pkg.pr.new/@vue/reactivity@14263
yarn add https://pkg.pr.new/@vue/[email protected]

@vue/runtime-core

pnpm add https://pkg.pr.new/@vue/runtime-core@14263
npm i https://pkg.pr.new/@vue/runtime-core@14263
yarn add https://pkg.pr.new/@vue/[email protected]

@vue/runtime-dom

pnpm add https://pkg.pr.new/@vue/runtime-dom@14263
npm i https://pkg.pr.new/@vue/runtime-dom@14263
yarn add https://pkg.pr.new/@vue/[email protected]

@vue/server-renderer

pnpm add https://pkg.pr.new/@vue/server-renderer@14263
npm i https://pkg.pr.new/@vue/server-renderer@14263
yarn add https://pkg.pr.new/@vue/[email protected]

@vue/shared

pnpm add https://pkg.pr.new/@vue/shared@14263
npm i https://pkg.pr.new/@vue/shared@14263
yarn add https://pkg.pr.new/@vue/[email protected]

vue

pnpm add https://pkg.pr.new/vue@14263
npm i https://pkg.pr.new/vue@14263
yarn add https://pkg.pr.new/[email protected]

@vue/compat

pnpm add https://pkg.pr.new/@vue/compat@14263
npm i https://pkg.pr.new/@vue/compat@14263
yarn add https://pkg.pr.new/@vue/[email protected]

commit: 0120fd2

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds support for props modifiers (^ and .) in render functions for server-side rendering (SSR), addressing issue #14262. These modifiers allow developers to explicitly control whether props are set as HTML attributes or DOM properties when using render functions with the h() API.

  • Implements applyModifiers function to handle ^ (force as attribute) and . (force as property/skip) modifiers
  • Integrates modifier handling into the ssrRenderAttrs function
  • Adds integration test to verify the modifier behavior in SSR context

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
packages/server-renderer/src/helpers/ssrRenderAttrs.ts Adds applyModifiers function and integrates it into ssrRenderAttrs to handle ^ and . modifiers, stripping the prefix for attribute rendering or skipping props marked with .
packages/server-renderer/tests/render.spec.ts Adds integration test verifying that ^attr renders as an attribute and .prop is correctly ignored in SSR output

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
packages/server-renderer/__tests__/render.spec.ts (1)

1232-1248: Test correctly validates the core functionality.

The test case properly verifies that:

  • ^attr modifier strips the ^ prefix and renders as an attribute
  • .prop modifier causes the prop to be ignored in SSR output
Optional: Consider expanding test coverage

Additional test cases could improve confidence:

 test('props modifiers in render functions', async () => {
   const app = createApp({
     setup() {
       return () =>
         h(
           'div',
           {
             '^attr': 'attr',
             '.prop': 'prop',
           },
           'Functional Component',
         )
     },
   })
   const html = await render(app)
   expect(html).toBe(`<div attr="attr">Functional Component</div>`)
 })
+
+test('props modifiers with normal props', async () => {
+  const app = createApp({
+    setup() {
+      return () =>
+        h(
+          'div',
+          {
+            id: 'test',
+            '^data-attr': 'value',
+            '.domProp': 'ignored',
+            class: 'foo',
+          },
+          'Mixed',
+        )
+    },
+  })
+  const html = await render(app)
+  expect(html).toBe(`<div id="test" class="foo" data-attr="value">Mixed</div>`)
+})
packages/server-renderer/src/helpers/ssrRenderAttrs.ts (1)

27-35: Implementation correctly handles SSR modifier prefixes.

The logic properly transforms prop keys:

  • ^ prefix → strips prefix for attribute binding
  • . prefix → returns null to skip DOM properties in SSR
  • No prefix → passes through unchanged
Optional: Add defensive checks for edge cases

Consider handling edge cases where the key is only a prefix or has multiple prefixes:

 const applyModifiers = (prop: string): string | null => {
   if (prop.startsWith('^')) {
-    return prop.slice(1) // attribute binding
+    const stripped = prop.slice(1)
+    // Avoid empty keys or nested modifiers
+    return stripped && !stripped.startsWith('^') && !stripped.startsWith('.') 
+      ? stripped 
+      : prop.slice(1) // attribute binding
   }
   if (prop.startsWith('.')) {
     return null // prop should be ignored
   }
   return prop // normal prop
 }

However, these are unlikely edge cases in practice and the current implementation is sufficient for typical usage.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between aac7e18 and fea9186.

📒 Files selected for processing (2)
  • packages/server-renderer/__tests__/render.spec.ts
  • packages/server-renderer/src/helpers/ssrRenderAttrs.ts
🔇 Additional comments (1)
packages/server-renderer/src/helpers/ssrRenderAttrs.ts (1)

58-62: Modifier application is correctly integrated with Vue's documented behavior.

The implementation properly applies modifiers and skips props that return null. Props prefixed with . (DOM properties) are correctly skipped in SSR since DOM properties don't exist on the server—only HTML attributes are serialized. Props prefixed with ^ are rendered as HTML attributes with the prefix removed. This aligns with Vue 3's documented render-function prop modifier semantics:

  • . → DOM property (skipped in SSR)
  • ^ → HTML attribute (rendered in SSR)

The placement in the else branch ensures modifiers apply to all dynamic props, excluding class, style, className, and ignored props.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@KazariEX KazariEX added scope: ssr 🍰 p2-nice-to-have Priority 2: this is not breaking anything but nice to have it addressed. labels Dec 31, 2025
@edison1105
Copy link
Member

Thanks for the PR, and I've made small tweaks.

@edison1105 edison1105 added the ready to merge The PR is ready to be merged. label Jan 1, 2026
@edison1105 edison1105 changed the title fix(ssr): add support for props modifiers in render functions fix(ssr): handle props modifiers during render attrs Jan 1, 2026
@edison1105 edison1105 changed the title fix(ssr): handle props modifiers during render attrs fix(ssr): handle v-bind modifiers during render attrs Jan 1, 2026
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/server-renderer/__tests__/render.spec.ts (1)

1233-1249: Test validates basic modifier behavior.

The test correctly verifies that:

  • '^attr' renders as attr="attr" (attribute modifier)
  • '.prop' is omitted from output (property modifier)

This covers the primary use case from issue #14262.

Optional: Consider edge case coverage

To increase confidence, consider adding test cases for:

  • Special keys with modifiers: '^class': 'foo', '.style': {...}
  • Event handlers: '^onClick': handler, .onClick: handler (to confirm they're properly handled/skipped)
  • Boolean attributes: '^disabled': true (to verify correct rendering)

Example:

test('props modifiers with special keys', async () => {
  const html = await render(
    h('input', { '^class': 'btn', '^disabled': true, '.prop': 'x' })
  )
  expect(html).toBe('<input class="btn" disabled>')
})

However, these tests can be deferred if the current coverage is deemed sufficient.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 97035af and 43a462e.

📒 Files selected for processing (2)
  • packages/server-renderer/__tests__/render.spec.ts
  • packages/server-renderer/src/helpers/ssrRenderAttrs.ts
🧰 Additional context used
🧬 Code graph analysis (1)
packages/server-renderer/src/helpers/ssrRenderAttrs.ts (1)
packages/shared/src/general.ts (1)
  • isOn (15-19)
🔇 Additional comments (3)
packages/server-renderer/src/helpers/ssrRenderAttrs.ts (3)

32-32: LGTM: Loop variable mutation enabled.

Changing from const to let is necessary to allow the key reassignment on line 44 when stripping the ^ modifier.


36-40: LGTM: Property modifier handling.

The logic correctly skips keys starting with . since DOM properties cannot be set during SSR. The comment clearly explains the behavior.


43-44: LGTM: Attribute modifier handling.

The ^ prefix is correctly stripped to force the key to be rendered as an attribute. The comment matches the terminology used in the render function documentation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🍰 p2-nice-to-have Priority 2: this is not breaking anything but nice to have it addressed. ready to merge The PR is ready to be merged. scope: ssr

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Props modifiers in render functions do not handled properly on SSR

3 participants