Skip to content

fix(runtime-core): pass SVG/MathML namespace to patchProp during hydration#15050

Closed
masoudei wants to merge 2 commits into
vuejs:mainfrom
masoudei:main
Closed

fix(runtime-core): pass SVG/MathML namespace to patchProp during hydration#15050
masoudei wants to merge 2 commits into
vuejs:mainfrom
masoudei:main

Conversation

@masoudei

@masoudei masoudei commented Jul 7, 2026

Copy link
Copy Markdown

Description

hydrateElement in runtime-core/src/hydration.ts was passing undefined as the namespace parameter to patchProp when hydrating SVG and MathML elements. This caused SVG coordinate attributes (x1, y1, x2, y2, cx, cy, r, x, y, width, height) to be set as read-only DOM properties instead of via setAttribute, triggering console warnings on every SSR hydration of SVG content.

Root Cause

In hydrateElement, patchProp is called with undefined as the namespace argument:

patchProp(el, key, null, props[key], undefined, parentComponent)

In patchProp, the namespace is used to determine isSVG (namespace === 'svg'). When undefined, isSVG is falsy, and shouldSetAsProp falls through to the HTML path where return key in el returns true for SVG coordinate properties (they exist as read-only SVGAnimatedLength getters). Vue then attempts DOM property assignment (el.x1 = 50) instead of setAttribute, causing a TypeError.

Fix

Added namespace detection from the element itself in hydrateElement:

const elementNamespace = el.namespaceURI?.includes('svg') && el.tagName !== 'foreignObject'
  ? 'svg'
  : el.namespaceURI?.includes('MathML')
    ? 'mathml'
    : undefined

This is then passed to both patchProp call sites in hydrateElement, mirroring the same pattern already used by mountComponent (which correctly passes getContainerType(container)).

Tests Added

  • SVG element coordinate attributes are set as attributes not properties — verifies <line> elements with x1, y1, x2, y2 hydrate without warnings
  • SVG element circle coordinate attributes hydrate correctly — verifies <circle> elements with cx, cy, r hydrate without warnings

Both tests assert that:

  1. Values are correctly set as attributes via getAttribute()
  2. No Failed setting prop warnings were emitted

All 114 hydration tests pass.

Summary by CodeRabbit

  • Bug Fixes
    • Improved SSR hydration for SVG coordinate attributes (e.g., line and circle), ensuring x1/y1/x2/y2 and cx/cy/r are applied correctly.
    • Reduced “Failed setting prop” warnings during hydration for SVG-related elements, including foreignObject.
  • Tests
    • Added additional SSR hydration test coverage for SVG coordinate attributes and foreignObject sizing/positioning, verifying correct DOM hydration and absence of related warnings.

During SSR hydration, hydrateElement was passing undefined as the
namespace parameter to patchProp. This caused SVG coordinate attributes
(x1, y1, x2, y2, cx, cy, r, etc.) to be incorrectly set as read-only
DOM properties instead of via setAttribute, triggering console warnings.

Added elementNamespace detection from el.namespaceURI and pass it to
both patchProp call sites in hydrateElement.

Signed-off-by: Masoud Ehteshami <ehteshami.developer@gmail.com>
@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9f727e50-95f7-4b7a-983d-de52913a2055

📥 Commits

Reviewing files that changed from the base of the PR and between 4fc23f0 and 3604943.

📒 Files selected for processing (2)
  • packages/runtime-core/__tests__/hydration.spec.ts
  • packages/runtime-core/src/hydration.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/runtime-core/src/hydration.ts
  • packages/runtime-core/tests/hydration.spec.ts

📝 Walkthrough

Walkthrough

Updates hydration so SVG and MathML-aware prop setting uses the element’s own namespace during SSR hydration. Adds coverage for SVG line, circle, and foreignObject attribute hydration without “Failed setting prop” warnings.

Changes

SVG/MathML namespace hydration fix

Layer / File(s) Summary
Compute and pass elementNamespace during hydration
packages/runtime-core/src/hydration.ts
hydrateElement derives elementNamespace from el.namespaceURI with foreignObject handling, then passes it to patchProp in the main props loop and the onClick fast-path.
SVG coordinate attribute hydration tests
packages/runtime-core/__tests__/hydration.spec.ts
Adds hydration tests for SVG <line>, <circle>, and <foreignObject> attribute cases, confirming the attributes land correctly and no warning is emitted.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

  • vuejs/core#14837: Also adjusts namespace handling during hydration-related DOM recovery by threading namespace information into template adoption.

Suggested labels: scope: ssr, scope:hydration

🚥 Pre-merge checks | ✅ 5
✅ 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 accurately summarizes the main hydration fix of passing SVG/MathML namespaces to patchProp.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
packages/runtime-core/__tests__/hydration.spec.ts (1)

1597-1628: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add coverage for foreignObject own attributes.

These tests cover <line>/<circle> well but don't exercise a <foreignObject> element's own x/y/width/height attributes, which is the edge case where the current elementNamespace computation in hydration.ts (excluding foreignObject from the svg namespace) would regress.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/runtime-core/__tests__/hydration.spec.ts` around lines 1597 - 1628,
Add a hydration test for SVG foreignObject own attributes using
mountWithHydration and h, similar to the existing SVG coordinate tests. The new
test should render a foreignObject inside an svg with x, y, width, and height,
then assert those values are present as attributes on the element and that no
prop-setting warning is emitted. Reference the existing SVG hydration tests in
hydration.spec.ts and the foreignObject namespace handling in hydration.ts to
locate the edge case.
packages/runtime-core/src/hydration.ts (1)

396-400: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Prefer exact namespace match over substring .includes().

el.namespaceURI is a full URI (http://www.w3.org/2000/svg, http://www.w3.org/1998/Math/MathML). Substring matching is more fragile than exact equality and diverges from how svgNS/mathmlNS are compared elsewhere in the renderer. Consider comparing against the exact known namespace URIs instead of .includes().

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/runtime-core/src/hydration.ts` around lines 396 - 400, Update the
namespace detection in hydration logic to use exact namespace URI equality
instead of substring checks. In the elementNamespace selection in hydration.ts,
compare el.namespaceURI against the known SVG and MathML namespace
constants/URIs used elsewhere in the renderer, while preserving the
foreignObject exception. Keep the existing branching structure but replace the
.includes() checks with exact matches so the behavior is consistent with
svgNS/mathmlNS handling.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/runtime-core/__tests__/hydration.spec.ts`:
- Around line 1597-1628: Add a hydration test for SVG foreignObject own
attributes using mountWithHydration and h, similar to the existing SVG
coordinate tests. The new test should render a foreignObject inside an svg with
x, y, width, and height, then assert those values are present as attributes on
the element and that no prop-setting warning is emitted. Reference the existing
SVG hydration tests in hydration.spec.ts and the foreignObject namespace
handling in hydration.ts to locate the edge case.

In `@packages/runtime-core/src/hydration.ts`:
- Around line 396-400: Update the namespace detection in hydration logic to use
exact namespace URI equality instead of substring checks. In the
elementNamespace selection in hydration.ts, compare el.namespaceURI against the
known SVG and MathML namespace constants/URIs used elsewhere in the renderer,
while preserving the foreignObject exception. Keep the existing branching
structure but replace the .includes() checks with exact matches so the behavior
is consistent with svgNS/mathmlNS handling.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 24c21107-f7f4-4955-9630-33d6b27ba5b0

📥 Commits

Reviewing files that changed from the base of the PR and between c0606e9 and 4fc23f0.

📒 Files selected for processing (2)
  • packages/runtime-core/__tests__/hydration.spec.ts
  • packages/runtime-core/src/hydration.ts

@edison1105

Copy link
Copy Markdown
Member

The direction looks right, but this should compute the namespace after the <template> replacement path in hydrateElement. With <Transition appear>, el can start as the SSR <template> and later be replaced with its SVG content, so the current elementNamespace value can stay undefined for an SVG root and still hit the prop-assignment path.

@pkg-pr-new

pkg-pr-new Bot commented Jul 8, 2026

Copy link
Copy Markdown

Open in StackBlitz

@vue/compiler-core

pnpm add https://pkg.pr.new/@vue/compiler-core@15050
npm i https://pkg.pr.new/@vue/compiler-core@15050
yarn add https://pkg.pr.new/@vue/compiler-core@15050.tgz

@vue/compiler-dom

pnpm add https://pkg.pr.new/@vue/compiler-dom@15050
npm i https://pkg.pr.new/@vue/compiler-dom@15050
yarn add https://pkg.pr.new/@vue/compiler-dom@15050.tgz

@vue/compiler-sfc

pnpm add https://pkg.pr.new/@vue/compiler-sfc@15050
npm i https://pkg.pr.new/@vue/compiler-sfc@15050
yarn add https://pkg.pr.new/@vue/compiler-sfc@15050.tgz

@vue/compiler-ssr

pnpm add https://pkg.pr.new/@vue/compiler-ssr@15050
npm i https://pkg.pr.new/@vue/compiler-ssr@15050
yarn add https://pkg.pr.new/@vue/compiler-ssr@15050.tgz

@vue/reactivity

pnpm add https://pkg.pr.new/@vue/reactivity@15050
npm i https://pkg.pr.new/@vue/reactivity@15050
yarn add https://pkg.pr.new/@vue/reactivity@15050.tgz

@vue/runtime-core

pnpm add https://pkg.pr.new/@vue/runtime-core@15050
npm i https://pkg.pr.new/@vue/runtime-core@15050
yarn add https://pkg.pr.new/@vue/runtime-core@15050.tgz

@vue/runtime-dom

pnpm add https://pkg.pr.new/@vue/runtime-dom@15050
npm i https://pkg.pr.new/@vue/runtime-dom@15050
yarn add https://pkg.pr.new/@vue/runtime-dom@15050.tgz

@vue/server-renderer

pnpm add https://pkg.pr.new/@vue/server-renderer@15050
npm i https://pkg.pr.new/@vue/server-renderer@15050
yarn add https://pkg.pr.new/@vue/server-renderer@15050.tgz

@vue/shared

pnpm add https://pkg.pr.new/@vue/shared@15050
npm i https://pkg.pr.new/@vue/shared@15050
yarn add https://pkg.pr.new/@vue/shared@15050.tgz

vue

pnpm add https://pkg.pr.new/vue@15050
npm i https://pkg.pr.new/vue@15050
yarn add https://pkg.pr.new/vue@15050.tgz

@vue/compat

pnpm add https://pkg.pr.new/@vue/compat@15050
npm i https://pkg.pr.new/@vue/compat@15050
yarn add https://pkg.pr.new/@vue/compat@15050.tgz

commit: 4fc23f0

@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown

Size Report

Bundles

File Size Gzip Brotli
runtime-dom.global.prod.js 107 kB (+158 B) 40.4 kB (+50 B) 36.3 kB (+25 B)
vue.global.prod.js 165 kB (+158 B) 60.4 kB (+43 B) 53.7 kB (+47 B)

Usages

Name Size Gzip Brotli
createApp (CAPI only) 49 kB 19 kB 17.4 kB
createApp 57.1 kB 22.1 kB 20.2 kB
createSSRApp 61.8 kB (+158 B) 24 kB (+33 B) 21.8 kB (+23 B)
defineCustomElement 63.3 kB 24 kB 21.9 kB
overall 71.9 kB 27.5 kB 25.1 kB

…t in hydrateElement

Move SVG/MathML namespace detection after <template> replacement
so that el is the actual element, not a <template> placeholder
(relevant for <Transition appear>). Also fixes optional chaining
lint error (ES2016 target). Adds foreignObject hydration test.

Signed-off-by: Masoud Ehteshami <ehteshami.developer@gmail.com>
@masoudei

masoudei commented Jul 9, 2026

Copy link
Copy Markdown
Author

Addressed review feedback in 3604943:

edison1105: Moved elementNamespace computation after the template replacement block so the actual element (not a template placeholder) is used — relevant for Transition appear where el starts as template then gets replaced with SVG content.

CodeRabbit nitpick (optional chaining): Replaced ?. with ! (non-null assertion) to fix ES2016 target lint error, matching the existing isSVGContainer pattern.

CodeRabbit nitpick (foreignObject test): Added test verifying foreignObject own attributes (x, y, width, height) hydrate correctly through the HTML attribute path.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants