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 Degradation during SSR
- LiveNode / Live: Evaluates its builder closure exactly once during the render pass. No signal subscriptions or reactive effect listeners are retained on the server.
- ShowNode / Show: Evaluates its
when()predicate closure once. Renderschildif true orfallbackif false. - ForEachNode / ForEach: Evaluates its
items()collection closure once and renders each item viabuilderto static HTML. - MountNode / Mount: Renders its
childdirectly; lifecycle callbacks (onMount,onUnmount) are ignored during SSR. - RefNode: Renders its
child; DOM references are not attached during SSR. - ContextProviderNode: Provides ambient context values down the subtree using Dart Zones.
- ErrorBoundaryNode: Renders
builder(); if an exception is thrown, synchronously rendersfallback(error, stack). - PortalNode: Emits
<template data-bloom-portal="...">enclosing the portal subtree. - SuspenseNode: In synchronous renderToHtml, renders the
fallbacknode only. For asynchronous out-of-order streaming of Suspense boundaries, use renderToStreamWithSuspense.
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.
- 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>{}});
}