renderToHtml function
Renders a BloomNode descriptor tree synchronously to an HTML string.
This is the primary server-side rendering (SSR) and static site generation (SSG) entry point. It executes entirely in pure Dart and requires no browser DOM, Flutter runtime, or JS interop.
Reactive Node Hydration Markers during SSR
- LiveNode / Live: Evaluates its builder closure exactly once and
wraps the output in
<!--bloom:live-->markers. No signal subscriptions are retained on the server; the browser hydrates the existing nodes. - MemoNode / Memo: Evaluates once, wrapped in
<!--bloom:memo-->. - ShowNode / Show: Evaluates its
when()predicate once, renders the active branch wrapped in<!--bloom:show-->markers. - ForEachNode / ForEach: Evaluates its
items()collection once. Keyed lists wrap each item in<!--bloom:key=<key>-->markers inside a<!--bloom:foreach-->boundary so hydration can reconcile by key. - ErrorBoundaryNode: Renders
builder()wrapped in<!--bloom:error-boundary-->; on exception rendersfallbackinstead. - SuspenseNode: In synchronous renderToHtml, renders the
fallbackwrapped in<!--bloom:suspense-->markers. For asynchronous out-of-order streaming of Suspense boundaries, use renderToStreamWithSuspense. - MountNode / Mount: Renders its
childdirectly; lifecycle callbacks (onMount,onUnmount) are ignored during SSR (hydration runsonMount). - RefNode: Renders its
child; DOM references are not attached during SSR. - ContextProviderNode: Provides ambient context values down the subtree using Dart Zones.
- PortalNode: Emits
<template data-bloom-portal="...">enclosing the portal subtree.
Tag and Attribute Validation & Security
- Tag names and attribute names are strictly validated against alphanumeric identifier patterns and will throw an ArgumentError if an invalid name is encountered.
- Text content, class names, styles, and attribute values are automatically escaped via escapeHtml.
- Inline event attributes,
srcdoc, and executable URL schemes are rejected; use the typedon:event map for handlers. - Void elements (e.g.
<img>,<input>,<br>,<meta>,<link>) are emitted without closing tags.
final html = renderToHtml(
Div(
className: 'user-profile',
children: [
const H1(text: 'Account Details'),
P(text: 'Welcome, Alice'),
],
),
);
Implementation
String renderToHtml(BloomNode node) {
return runZoned(() {
final buf = StringBuffer();
_render(node, buf);
return buf.toString();
}, zoneValues: {_keyframesZoneKey: <String>{}});
}