Skip to content

Fix/custom element nullish prop coercion#14466

Closed
Liknox wants to merge 2 commits intovuejs:mainfrom
Liknox:fix/custom-element-nullish-prop-coercion
Closed

Fix/custom element nullish prop coercion#14466
Liknox wants to merge 2 commits intovuejs:mainfrom
Liknox:fix/custom-element-nullish-prop-coercion

Conversation

@Liknox
Copy link

@Liknox Liknox commented Feb 23, 2026

Summary

Fixes #14209

Custom element properties can accept various types. When a nullish value (null/undefined) or an empty string is provided, Vue should not coerce it according to the property's existing type.

In the previous implementation of patchDOMProp, the nullish and empty value coercion logic would check the type of el[key] and coerce it—even for custom elements:

  • null0 for number properties
  • null"" for string properties
  • ""true for boolean properties

While this was suitable for native HTML elements, it was incorrect for custom elements that can intentionally accept null/undefined or have varying types.

The fix avoids this type-based coercion for custom elements (tag.includes('-')), preserving the value while still removing the attribute if it's nullish.

Test Plan

  • Updated the existing "value for custom elements" test to expect null instead of a coerced ''
  • Added a new test for all three coercion scenarios: number, boolean, and string props on custom elements
  • All 220 runtime-dom tests pass

Summary by CodeRabbit

Bug Fixes: Custom elements maintain null and nullish property values as-is, while standard HTML elements retain existing coercion behavior.

Summary by CodeRabbit

  • Bug Fixes
    • Improved null value handling for custom elements. Properties now correctly preserve null values instead of being coerced to empty strings or type-based defaults.

@coderabbitai
Copy link

coderabbitai bot commented Feb 23, 2026

📝 Walkthrough

Walkthrough

The changes fix nullish value handling for custom element properties in the DOM patching module. Custom elements now preserve null values instead of coercing them based on property type, while standard elements maintain their existing coercion behavior.

Changes

Cohort / File(s) Summary
Custom Element Prop Coercion Logic
packages/runtime-dom/src/modules/props.ts
Added special handling for custom elements (tag names containing dash). Custom elements with null or empty string values now bypass type-based coercion, preserving nullish values. Non-custom elements retain previous coercion behavior for booleans, strings, and numbers.
Nullish Handling Test Coverage
packages/runtime-dom/__tests__/patchProps.spec.ts
Updated existing test to expect 'null' string instead of empty string when patching custom element value with null. Added new test block validating nullish preservation for dot-prefixed custom element properties (myNumber, myBool, myString).

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Suggested labels

ready to merge, :hammer: p3-minor-bug, scope: custom elements

Poem

🐰 Custom elements now dance with care,
When null comes knocking through the air,
No coercion tricks, no sleight of hand,
The null stays null, as Vue had planned! ✨

🚥 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 describes the main change: fixing custom element nullish prop coercion to prevent unwanted type-based coercion.
Linked Issues check ✅ Passed The PR fully addresses issue #14209 by disabling type-based coercion for custom elements, preserving nullish values while still removing attributes as needed.
Out of Scope Changes check ✅ Passed All changes are directly related to fixing custom element nullish prop coercion; no unrelated modifications detected.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


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.

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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/runtime-dom/__tests__/patchProps.spec.ts`:
- Around line 67-70: The test assertion expects the string 'null' but patchProp
-> patchDOMProp for custom elements assigns the primitive null to el.value, so
update the assertion in patchProps.spec.ts to expect primitive null
(expect(el.value).toBe(null)) and ensure other assertions (e.g.,
expect(el.getAttribute('value')) and expect(el.setterCalled)) remain unchanged;
locate the test call to patchProp(el, 'value', null, null) and replace the
string assertion accordingly.

Comment on lines 67 to 70
patchProp(el, 'value', null, null)
expect(el.value).toBe('')
expect(el.value).toBe('null')
expect(el.setterCalled).toBe(2)
expect(el.getAttribute('value')).toBe(null)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Test assertion uses string 'null' instead of primitive null.

Tracing the new code path: patchProp(el, 'value', null, null) calls patchDOMProp with value = null. The key === 'value' early-return block (lines 27–55 of props.ts) is skipped for custom elements, so the new CE branch runs: needRemove = true, value is not coerced, and el.value = null is called directly. The custom setter stores null in this._value, so the getter returns null — not 'null'.

The PR objectives also state: "existing 'value for custom elements' expectation changed to null" — i.e. the primitive, not the string.

🐛 Fix the assertion
-    expect(el.value).toBe('null')
+    expect(el.value).toBe(null)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
patchProp(el, 'value', null, null)
expect(el.value).toBe('')
expect(el.value).toBe('null')
expect(el.setterCalled).toBe(2)
expect(el.getAttribute('value')).toBe(null)
patchProp(el, 'value', null, null)
expect(el.value).toBe(null)
expect(el.setterCalled).toBe(2)
expect(el.getAttribute('value')).toBe(null)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/runtime-dom/__tests__/patchProps.spec.ts` around lines 67 - 70, The
test assertion expects the string 'null' but patchProp -> patchDOMProp for
custom elements assigns the primitive null to el.value, so update the assertion
in patchProps.spec.ts to expect primitive null (expect(el.value).toBe(null)) and
ensure other assertions (e.g., expect(el.getAttribute('value')) and
expect(el.setterCalled)) remain unchanged; locate the test call to patchProp(el,
'value', null, null) and replace the string assertion accordingly.

@edison1105
Copy link
Member

Closing as duplicate of #14459

@edison1105 edison1105 closed this Feb 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Invalid nullish value handling in patchDOMProp for custom elements.

2 participants