Skip to content

fix: align SSR and client text whitespace - #610

Merged
Mohamed Mansour (mohamedmansour) merged 1 commit into
mainfrom
mohamedmansour-ssr-whitespace-consistency
Oct 20, 2026
Merged

Mohamed Mansour (mohamedmansour) merged 1 commit into
mainfrom
mohamedmansour-ssr-whitespace-consistency

Conversation

@mohamedmansour

Copy link
Copy Markdown
Contributor

Why

Valid multiline text bindings can report hydration mismatches even when component values match SSR. The server discarded newline-only static pieces around bindings while client metadata retained them, so later client updates also introduced indentation absent from the initial render.

Changes

  • Preserve every static piece within an emitted text run without trimming property values or changing mismatch diagnostics.
  • Keep text-run edges consistent across components, conditionals, and repeats. Compile native source-newline, entity, and initial-LF semantics for pre/textarea/listing into client text parts.
  • Reuse owned text buffers, normalize CRLF with one output buffer, and perform boundary trimming in the existing compiler pass instead of another HTML walk.
  • Extend the canonical hydration-mismatch fixture with native and generic slotted elements, client-created parity, genuine mismatch detection, whitespace-bearing updates, and DOM/slot identity checks. Update rendering docs and the hydration contract.

No browser runtime, handler, dependency, or protobuf schema changes are required. The generic regression reproduces with published SDK/framework 3.0.3 and baseline bad3aa0454fe976b5ba5c3c6ebb7b3dd3c820863. Candidate-generated protocols also render and hydrate with the untouched published 3.0.3 handler and framework.

Tradeoffs and efficiency

Retaining authored whitespace can change initial inline spacing or whitespace-sensitive rendering. In the public reproduction, uncompressed HTML grows from 1,627 to 1,667 bytes and the protocol from 1,944 to 1,990 bytes; hydration metadata stays at 948 bytes.

The following release allocation measurements compare the optimized implementation with the initial already-correct implementation of this fix, not unmodified main. The baseline is bad3aa0454fe976b5ba5c3c6ebb7b3dd3c820863 plus source patch SHA-256 e36911fff0e57027097e95f9718e0c5e721b8091c9d5a18a214a459047d0b6fe.

Compiler workload Allocation calls before After Peak additional live requested bytes
100 LF bindings 2,063 1,863 98,682, unchanged
100 CRLF bindings 2,463 2,063 99,882, unchanged

All 25 allocation samples per variant agreed. Generated metadata and the public fixture HTML remain byte-for-byte identical to the first correct implementation. Timing was affected by shared-host noise; no general throughput or RSS improvement is claimed.

Validation

  • cargo xtask check passed immediately before committing, including docs, using the matching local Press sidecar.
  • 793 parser unit tests plus its doc test passed.
  • 17 focused Playwright tests passed across hydration-mismatch, text-only-repeat, and raw-html-for-attr.
  • The real-browser regression failed before the fix; genuine state-versus-SSR mismatches still warn afterward.
  • Framework E2E typechecking, buffer-reuse guards, mixed-newline/UTF-8 tests, and the comment-boundary regression passed.

Preserve static whitespace within bound text runs and compile native newline semantics consistently. Reuse text buffers and integrate boundary trimming into the existing compiler pass. Add browser regressions, allocation guards, focused benchmarks, and rendering documentation.

Copilot-Session: 0e5ecabb-9111-44ba-978b-ea545584387f

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Approval recommended

No blocking issues remain, and focused regression coverage supports the compiler changes across SSR, hydration, client creation, and updates.

No open issues 🎉

What changed in this PR

Aligns WebUI’s server-rendered text with client hydration and updates, preventing whitespace-only mismatches without changing runtime or protocol schemas.

Changes:

  • Preserves static whitespace around bindings and compiles native HTML newline semantics.
  • Reuses text buffers and adds whitespace benchmarks.
  • Expands hydration regressions and documents the text contract.
File Description
specs/​hydration.md Defines whitespace and newline parity.
packages/​webui-framework/​tests/​fixtures/​hydration-mismatch/​state.json Adds whitespace regression data.
packages/​webui-framework/​tests/​fixtures/​hydration-mismatch/​src/​whitespace-slot/​whitespace-slot.html Adds a slotted test component.
packages/​webui-framework/​tests/​fixtures/​hydration-mismatch/​src/​mismatch-whitespace/​mismatch-whitespace.html Exercises native elements and text boundaries.
packages/​webui-framework/​tests/​fixtures/​hydration-mismatch/​src/​index.html Includes the whitespace fixture.
packages/​webui-framework/​tests/​fixtures/​hydration-mismatch/​hydration-mismatch.spec.ts Tests rendering parity, updates, identity, and genuine mismatches.
packages/​webui-framework/​tests/​fixtures/​hydration-mismatch/​element.ts Registers whitespace test components.
docs/​guide/​concepts/​hydration.md Explains authored whitespace behavior.
crates/​webui-parser/​src/​plugin/​webui/​named_repeats.rs Initializes the boundary-formatting flag.
crates/​webui-parser/​src/​plugin/​webui.rs Aligns compiled text semantics and adds regression tests.
crates/​webui-parser/​src/​lib.rs Preserves static pieces within emitted text runs.
crates/​webui-parser/​benches/​parser_bench.rs Benchmarks newline and entity workloads.

Review effort: Balanced

@mddinbox mddinbox left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewed 155dc57; complete pinned three-dot diff and all PR checks passed.

@mohamedmansour
Mohamed Mansour (mohamedmansour) merged commit dc0ccd9 into main Oct 20, 2026
42 checks passed
@mohamedmansour
Mohamed Mansour (mohamedmansour) deleted the mohamedmansour-ssr-whitespace-consistency branch October 20, 2026 20:10
Mohamed Mansour (mohamedmansour) added a commit that referenced this pull request Oct 20, 2026
## Release

Bumps WebUI to `3.0.4`.

Previous release tag: `v3.0.3`

## Changes since `v3.0.3`

Fixes:

- Omit whole-binding attributes for missing or null server values and
remove them for null or undefined client values, preserving explicit
empty strings, zero, false, and mixed attributes so optional native form
constraints behave correctly (fix: omit nullish whole-binding attributes
#605 by @mohamedmansour).
- Preserve authored static whitespace around text bindings so SSR,
hydration, reactive updates, and client-created content agree, including
native newline and entity semantics in whitespace-sensitive elements.
Genuine state mismatches still warn; retaining authored whitespace may
change initial inline spacing (fix: align SSR and client text whitespace
#610 by @mohamedmansour).

Docs:

- Clarify nullish attribute behavior in the state-management guide and
AI authoring reference (fix: omit nullish whole-binding attributes #605
by @mohamedmansour).
- Update rendering guidance and the hydration contract for consistent
text whitespace across server and client rendering (fix: align SSR and
client text whitespace #610 by @mohamedmansour).

Maintenance:

- Adopt existing SSR nodes using a compact compiled DOM map, defer
template parsing until new content is created, and deduplicate repeated
binding work within dependency roots. In 20 matched Edge runs of a
120-card SSR workload, median hydration wall time fell from 6.70 to 6.10
ms, startup JavaScript CPU from 8.220 to 7.386 ms, and initial retained
V8 heap from 2,151,838 to 2,127,958 bytes. These are workload-specific
improvements: interaction JavaScript CPU increased on that workload, and
first-use creation still pays deferred parsing cost (perf: reduce SSR
hydration and repeated binding work #609 by @mohamedmansour).

## Deployment

Rebuild `protocol.bin` and the browser bundle together for the compiled
DOM-map hydration change; no legacy metadata fallback is provided.

## Validation

- `cargo xtask check`

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 4c7d731c-f2a7-400c-9178-da97b9c445e4
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