Skip to content

Fix hydration when we have defaultValue or value on a textarea#5081

Merged
JoviDeCroock merged 1 commit into
v10.xfrom
fix-default-value
May 2, 2026
Merged

Fix hydration when we have defaultValue or value on a textarea#5081
JoviDeCroock merged 1 commit into
v10.xfrom
fix-default-value

Conversation

@JoviDeCroock

@JoviDeCroock JoviDeCroock commented May 1, 2026

Copy link
Copy Markdown
Member

Fixes #5080.

Summary

  • Preserve SSR textarea text during hydration when using defaultValue
  • Re-apply controlled textarea value during hydration
  • Add hydration coverage for textarea defaultValue and value

@github-actions

github-actions Bot commented May 1, 2026

Copy link
Copy Markdown

📊 Tachometer Benchmark Results

Summary

duration

  • create10k: unsure 🔍 -1% - +1% (-6.03ms - +11.12ms)
    preact-local vs preact-main
  • filter-list: unsure 🔍 -0% - +0% (-0.08ms - +0.05ms)
    preact-local vs preact-main
  • hydrate1k: slower ❌ 0% - 2% (0.05ms - 1.31ms)
    preact-local vs preact-main
  • many-updates: unsure 🔍 -3% - +1% (-0.44ms - +0.19ms)
    preact-local vs preact-main
  • replace1k: unsure 🔍 -1% - +2% (-0.43ms - +1.09ms)
    preact-local vs preact-main
  • text-update: unsure 🔍 -7% - +0% (-0.13ms - +0.01ms)
    preact-local vs preact-main
  • todo: slower ❌ 0% - 3% (0.14ms - 1.01ms)
    preact-local vs preact-main
  • update10th1k: unsure 🔍 -4% - +4% (-1.22ms - +1.19ms)
    preact-local vs preact-main

usedJSHeapSize

  • create10k: unsure 🔍 +0% - +0% (+0.00ms - +0.00ms)
    preact-local vs preact-main
  • filter-list: unsure 🔍 -1% - +1% (-0.01ms - +0.01ms)
    preact-local vs preact-main
  • hydrate1k: unsure 🔍 -1% - +12% (-0.04ms - +0.66ms)
    preact-local vs preact-main
  • many-updates: unsure 🔍 +0% - +0% (+0.00ms - +0.01ms)
    preact-local vs preact-main
  • replace1k: unsure 🔍 -0% - +0% (-0.00ms - +0.01ms)
    preact-local vs preact-main
  • text-update: unsure 🔍 -1% - +4% (-0.01ms - +0.04ms)
    preact-local vs preact-main
  • todo: unsure 🔍 -0% - +0% (-0.00ms - +0.00ms)
    preact-local vs preact-main
  • update10th1k: unsure 🔍 -0% - +0% (-0.01ms - +0.01ms)
    preact-local vs preact-main

Results

create10k

duration

VersionAvg timevs preact-localvs preact-main
preact-local857.71ms - 871.90ms-unsure 🔍
-1% - +1%
-6.03ms - +11.12ms
preact-main857.44ms - 867.07msunsure 🔍
-1% - +1%
-11.12ms - +6.03ms
-

usedJSHeapSize

VersionAvg timevs preact-localvs preact-main
preact-local19.21ms - 19.21ms-unsure 🔍
+0% - +0%
+0.00ms - +0.00ms
preact-main19.20ms - 19.20msunsure 🔍
-0% - -0%
-0.00ms - -0.00ms
-
filter-list

duration

VersionAvg timevs preact-localvs preact-main
preact-local16.52ms - 16.62ms-unsure 🔍
-0% - +0%
-0.08ms - +0.05ms
preact-main16.54ms - 16.64msunsure 🔍
-0% - +0%
-0.05ms - +0.08ms
-

usedJSHeapSize

VersionAvg timevs preact-localvs preact-main
preact-local1.56ms - 1.58ms-unsure 🔍
-1% - +1%
-0.01ms - +0.01ms
preact-main1.56ms - 1.58msunsure 🔍
-1% - +1%
-0.01ms - +0.01ms
-
hydrate1k

duration

VersionAvg timevs preact-localvs preact-main
preact-local58.09ms - 59.09ms-slower ❌
0% - 2%
0.05ms - 1.31ms
preact-main57.52ms - 58.30msfaster ✔
0% - 2%
0.05ms - 1.31ms
-

usedJSHeapSize

VersionAvg timevs preact-localvs preact-main
preact-local5.61ms - 6.16ms-unsure 🔍
-1% - +12%
-0.04ms - +0.66ms
preact-main5.35ms - 5.80msunsure 🔍
-11% - +1%
-0.66ms - +0.04ms
-
many-updates

duration

VersionAvg timevs preact-localvs preact-main
preact-local16.48ms - 16.53ms-unsure 🔍
-3% - +1%
-0.44ms - +0.19ms
preact-main16.32ms - 16.94msunsure 🔍
-1% - +3%
-0.19ms - +0.44ms
-

usedJSHeapSize

VersionAvg timevs preact-localvs preact-main
preact-local3.73ms - 3.74ms-unsure 🔍
+0% - +0%
+0.00ms - +0.01ms
preact-main3.73ms - 3.73msunsure 🔍
-0% - -0%
-0.01ms - -0.00ms
-
replace1k
  • Browser: chrome-headless
  • Sample size: 100
  • Built by: CI #5481
  • Commit: 0ea30bd

duration

VersionAvg timevs preact-localvs preact-main
preact-local58.43ms - 59.58ms-unsure 🔍
-1% - +2%
-0.43ms - +1.09ms
preact-main58.19ms - 59.18msunsure 🔍
-2% - +1%
-1.09ms - +0.43ms
-

usedJSHeapSize

VersionAvg timevs preact-localvs preact-main
preact-local3.01ms - 3.02ms-unsure 🔍
-0% - +0%
-0.00ms - +0.01ms
preact-main3.01ms - 3.01msunsure 🔍
-0% - +0%
-0.01ms - +0.00ms
-

run-warmup-0

VersionAvg timevs preact-localvs preact-main
preact-local28.44ms - 29.29ms-unsure 🔍
-3% - +1%
-0.78ms - +0.40ms
preact-main28.65ms - 29.46msunsure 🔍
-1% - +3%
-0.40ms - +0.78ms
-

run-warmup-1

VersionAvg timevs preact-localvs preact-main
preact-local33.08ms - 34.47ms-unsure 🔍
-3% - +3%
-1.10ms - +0.91ms
preact-main33.14ms - 34.61msunsure 🔍
-3% - +3%
-0.91ms - +1.10ms
-

run-warmup-2

VersionAvg timevs preact-localvs preact-main
preact-local33.00ms - 34.35ms-unsure 🔍
-3% - +2%
-1.15ms - +0.62ms
preact-main33.37ms - 34.52msunsure 🔍
-2% - +3%
-0.62ms - +1.15ms
-

run-warmup-3

VersionAvg timevs preact-localvs preact-main
preact-local27.00ms - 27.55ms-unsure 🔍
-2% - +1%
-0.61ms - +0.14ms
preact-main27.25ms - 27.77msunsure 🔍
-1% - +2%
-0.14ms - +0.61ms
-

run-warmup-4

VersionAvg timevs preact-localvs preact-main
preact-local23.12ms - 24.16ms-unsure 🔍
-2% - +4%
-0.46ms - +1.02ms
preact-main22.83ms - 23.90msunsure 🔍
-4% - +2%
-1.02ms - +0.46ms
-

run-final

VersionAvg timevs preact-localvs preact-main
preact-local21.76ms - 22.16ms-unsure 🔍
-2% - +1%
-0.43ms - +0.17ms
preact-main21.87ms - 22.32msunsure 🔍
-1% - +2%
-0.17ms - +0.43ms
-
text-update
  • Browser: chrome-headless
  • Sample size: 220
  • Built by: CI #5481
  • Commit: 0ea30bd

duration

VersionAvg timevs preact-localvs preact-main
preact-local1.79ms - 1.88ms-unsure 🔍
-7% - +0%
-0.13ms - +0.01ms
preact-main1.85ms - 1.95msunsure 🔍
-0% - +7%
-0.01ms - +0.13ms
-

usedJSHeapSize

VersionAvg timevs preact-localvs preact-main
preact-local1.01ms - 1.05ms-unsure 🔍
-1% - +4%
-0.01ms - +0.04ms
preact-main1.00ms - 1.03msunsure 🔍
-4% - +1%
-0.04ms - +0.01ms
-
todo

duration

VersionAvg timevs preact-localvs preact-main
preact-local35.95ms - 36.63ms-slower ❌
0% - 3%
0.14ms - 1.01ms
preact-main35.45ms - 35.99msfaster ✔
0% - 3%
0.14ms - 1.01ms
-

usedJSHeapSize

VersionAvg timevs preact-localvs preact-main
preact-local1.26ms - 1.26ms-unsure 🔍
-0% - +0%
-0.00ms - +0.00ms
preact-main1.26ms - 1.26msunsure 🔍
-0% - +0%
-0.00ms - +0.00ms
-
update10th1k

duration

VersionAvg timevs preact-localvs preact-main
preact-local31.03ms - 32.83ms-unsure 🔍
-4% - +4%
-1.22ms - +1.19ms
preact-main31.15ms - 32.75msunsure 🔍
-4% - +4%
-1.19ms - +1.22ms
-

usedJSHeapSize

VersionAvg timevs preact-localvs preact-main
preact-local2.99ms - 3.01ms-unsure 🔍
-0% - +0%
-0.01ms - +0.01ms
preact-main2.99ms - 3.00msunsure 🔍
-0% - +0%
-0.01ms - +0.01ms
-

tachometer-reporter-action v2 for CI

@github-actions

github-actions Bot commented May 1, 2026

Copy link
Copy Markdown

Size Change: +158 B (+0.2%)

Total Size: 79.2 kB

📦 View Changed
Filename Size Change
dist/preact.js 4.78 kB +22 B (+0.46%)
dist/preact.min.js 4.8 kB +24 B (+0.5%)
dist/preact.min.module.js 4.81 kB +23 B (+0.48%)
dist/preact.min.umd.js 4.82 kB +24 B (+0.5%)
dist/preact.mjs 4.8 kB +21 B (+0.44%)
dist/preact.module.js 4.8 kB +21 B (+0.44%)
dist/preact.umd.js 4.83 kB +23 B (+0.48%)
ℹ️ View Unchanged
Filename Size
compat/dist/compat.js 4.19 kB
compat/dist/compat.mjs 4.11 kB
compat/dist/compat.module.js 4.11 kB
compat/dist/compat.umd.js 4.25 kB
debug/dist/debug.js 3.85 kB
debug/dist/debug.mjs 3.85 kB
debug/dist/debug.module.js 3.85 kB
debug/dist/debug.umd.js 3.93 kB
devtools/dist/devtools.js 259 B
devtools/dist/devtools.mjs 274 B
devtools/dist/devtools.module.js 274 B
devtools/dist/devtools.umd.js 345 B
hooks/dist/hooks.js 1.52 kB
hooks/dist/hooks.mjs 1.56 kB
hooks/dist/hooks.module.js 1.56 kB
hooks/dist/hooks.umd.js 1.59 kB
jsx-runtime/dist/jsxRuntime.js 1.01 kB
jsx-runtime/dist/jsxRuntime.mjs 985 B
jsx-runtime/dist/jsxRuntime.module.js 985 B
jsx-runtime/dist/jsxRuntime.umd.js 1.08 kB
test-utils/dist/testUtils.js 473 B
test-utils/dist/testUtils.mjs 477 B
test-utils/dist/testUtils.module.js 477 B
test-utils/dist/testUtils.umd.js 555 B

compressed-size-action

Comment thread src/diff/index.js
// If excessDomChildren was not null, repopulate it with the current element's children:
excessDomChildren = excessDomChildren && slice.call(dom.childNodes);
excessDomChildren =
nodeType == 'textarea' && newProps.defaultValue != NULL

@JoviDeCroock JoviDeCroock May 1, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Basically makes us ignore children of a textarea so that we don't remove them in HTML the children of a textarea are its defaultValue, I know confusing right?

In HTML, the initial content of a <textarea> is specified between its opening and closing tags, not as a value attribute.

Source

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

TIL!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

TIL too, I swore value was valid

Comment thread src/diff/index.js

// As above, don't diff props during hydration
if (!isHydrating) {
if (!isHydrating || nodeType == 'textarea') {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

This ensures that we go into value re-hydration for textareas

@JoviDeCroock JoviDeCroock requested a review from rschristian May 1, 2026 06:44

@marvinhagemeister marvinhagemeister left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM

@JoviDeCroock JoviDeCroock merged commit cae8d3a into v10.x May 2, 2026
14 checks passed
@JoviDeCroock JoviDeCroock deleted the fix-default-value branch May 2, 2026 05:15
JoviDeCroock added a commit that referenced this pull request May 16, 2026
* Fix incorrect DOM order with conditional ContextProvider and inner keys (#5065) (#5067)

When a VNode is moved via INSERT_VNODE during diffChildren, its old _dom
pointer becomes a stale positional reference. Nested diffs that call
getDomSibling would traverse the old VNode tree and find this stale _dom,
causing subsequent DOM insertions at the wrong position.

Clear the old VNode's _dom after insert so getDomSibling skips moved
VNodes and finds the correct insertion point.

* Create a unique event-clock for each Preact instance on a page. (#5068)

* Fix migrations when we have defaultValue or value on a textarea (#5081)

* Add CODEOWNERS for GitHub configuration (#5088)

* chore: remove unused Fragment import in forwardported test
@JoviDeCroock JoviDeCroock mentioned this pull request May 17, 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.

3 participants