Runtime & Infrastructure
Hydration
Hydration — Runtime & Infrastructure documentation for BCP Framework.
Last updated: 2026-08-29T01:11:41
Hydration and deterministic rendering
BCP uses server-side rendering for the initial HTML and React hydration in the browser. The server-rendered tree and the first client-rendered tree must produce the same element attributes and text.
Development transform parity
BCP Framework 0.1.21 fixes a development hydration mismatch caused by the server and client using different JSX transform semantics.
A common trigger is a multiline quoted JSX attribute:
export default function Page() {
return (
<main
className="
min-h-screen
bg-white
text-slate-950
"
>
Hello
</main>
);
}Before 0.1.21, development SSR loaded TSX through the server runtime while the client React Refresh path transformed the same module with @babel/preset-react. Babel's JSX transform normalizes whitespace in multiline quoted JSX attributes, so the client could receive a value such as:
" min-h-screen bg-white text-slate-950 "while SSR produced the original multiline value:
"\n min-h-screen\n bg-white\n text-slate-950\n "Those class lists are visually equivalent to CSS but they are different JavaScript strings, so React reports a hydration mismatch.
BCP 0.1.21 keeps Babel in the development pipeline for TypeScript stripping and React Refresh registration, but Babel no longer compiles JSX. JSX is handed to esbuild with the development JSX runtime enabled. This keeps development JSX semantics aligned with the framework's esbuild-based client compilation and avoids Babel rewriting multiline attribute values before hydration.
Windows CRLF support
BCP Framework 0.1.20 added source line-ending normalization for the development React Refresh path.
Application source is normalized from CRLF (\r\n) and standalone CR (\r) to LF (\n) before Babel processes development modules. This prevents Windows line endings from introducing carriage-return differences between SSR and the client bundle.
The 0.1.20 fix correctly removed carriage-return mismatches, but a separate Babel JSX whitespace normalization issue remained for multiline quoted JSX attributes. That remaining transform-parity issue is addressed by 0.1.21.
Developers do not need to rewrite multiline className values as one-line strings to work around either framework issue.
Supported multiline patterns
Both of these patterns are valid application code:
<div
className="
min-h-screen
bg-white
text-slate-950
"
/>and:
<div
className={`
min-h-screen
bg-white
text-slate-950
`}
/>BCP should hydrate them deterministically without requiring application-specific whitespace workarounds.
What BCP fixes automatically
The framework fixes deterministic source-transform differences. It does not hide genuine hydration differences caused by application behavior.
BCP applications should still avoid producing different initial values on the server and client from code such as:
const value = Date.now();const value = Math.random();or:
const browserOnly =
typeof window !== "undefined";when those values directly change the initial rendered markup.
Other common application-level causes include:
- locale-dependent formatting that differs between server and browser,
- data that changes between SSR and hydration without a serialized snapshot,
- browser-only state initialized from
localStorage,sessionStorageormatchMedia, - invalid HTML nesting,
- browser extensions that modify the DOM before React hydrates it.
Recommended pattern for browser-only state
If a value genuinely depends on the browser, initialize the server/client render deterministically and update it after mount.
"use client";
import {
useEffect,
useState,
} from "react";
export default function BrowserValue() {
const [ready, setReady] =
useState(false);
useEffect(
() => {
setReady(true);
},
[]
);
return (
<span>
{ready
? "Browser ready"
: "Loading"}
</span>
);
}Development pipeline
The 0.1.21 development client transform is intentionally split by responsibility:
application TS/TSX
↓
line-ending normalization
↓
Babel
- remove TypeScript syntax
- inject React Refresh registrations
- preserve JSX
↓
esbuild
- compile JSX
- use development JSX runtime
- bundle application modules
↓
React hydrationThe key rule is that React Refresh instrumentation must not change the semantic value of JSX attributes compared with SSR.
Production client compilation already uses the esbuild production pipeline and does not use the development React Refresh Babel transform.
Troubleshooting
If React still reports a hydration mismatch after upgrading to a BCP release containing the 0.1.21 fix:
- stop the BCP dev server,
- remove
.bcp-framework/, - start
bcp devagain, - hard-refresh the browser,
- inspect the first differing server/client value in the React hydration warning,
- check for request-time, random, locale, browser-only or externally changing values.
For framework diagnostics, compare the initial SSR HTML with the generated development client bundle. If the same static JSX attribute produces different strings, treat it as a framework transform-parity regression.
Do not use suppressHydrationWarning as a general fix. It should only be used when a difference is intentional and understood.
Regression coverage
The framework test suite contains a Windows-style CRLF fixture using the same multiline quoted JSX className pattern that exposed the issue in a real BCP application.
The regression test extracts the generated className value from the development bundle and verifies that its semantic string value still contains the expected line breaks and indentation instead of Babel's collapsed whitespace form.