← Summary · 41 test pages · measured at 31b26561f1
HTML-first examples
These are the 41 server-rendered test pages used to check that the reference implementation (final version df356153a6) neither breaks hydration nor changes what the user sees. Each name opens a live demo with the flag on, and the link beside it shows the page's source. Replace /all/ with /off/ in a demo's address to see the same page with the flag off.
The gzip column is the size of the whole document with the flag on, then the change from the flag-off size. Most pages shrink. A few grow slightly, either because they have little static markup to remove or because the registry row costs more than it saves. Across all 41 pages gzipped bytes fall from 1,023,906 to 586,852 (−42.7%) and inlined Flight from 9,280,754 to 1,977,902 bytes (−78.7%); the median page saves 7.8%.
static
| Example | gzip vs stock |
|---|---|
attributes-styles-ariaAttribute, style and ARIA variety Server host elements with many attribute kinds: className, style objects (numeric values, CSS custom properties), data-/aria- attributes, boolean attributes (hidden, inert, open), tabIndex, img srcSet/sizes/loading, link target/rel, table colSpan/rowSpan, and SVG viewBox with camelCase attributes. One client counter. | 43040 (-18.7%) |
blog-articleLong blog article Long article with headings, many paragraphs, code blocks, images with alt/width/height and blockquotes, plus one small client like button. | 22320 (-17.4%) |
dangerous-inner-htmldangerouslySetInnerHTML blocks Server-rendered dangerouslySetInnerHTML blocks containing rich HTML (nested tags, entities, attributes, inline SVG) mixed with normal JSX siblings. One client counter. | 9891 (-50.1%) |
docs-sidebar-tocDocs page with sidebar tree and table of contents Large server-rendered sidebar navigation tree, an in-page table of contents and content sections, plus one client button that toggles an attribute on itself. | 16405 (-58.5%) |
form-controls-uncontrolledUncontrolled form controls Server-rendered forms with uncontrolled inputs: defaultValue, defaultChecked, select with optgroup, textarea, radio groups, labels, fieldsets, disabled and readOnly controls. One client counter. | 25203 (-4.6%) |
fragments-arrays-keysFragments, arrays, keys and empty children Nested and keyed Fragments, keyed arrays, unkeyed text arrays, null/false/undefined/""/0 children, adjacent text nodes, plus one client leaf. | 803 (-0.5%) |
full-refresh-focusTyped input value must survive a full refetch Static server form markup with an uncontrolled input; the user types, the page is refetched (client navigation with full Flight), and the typed value must survive. | 668 (+0.3%) |
head-hoistablesHead hoistables from deep in the body Server components deep inside the body render <title>, <meta>, <link rel=preload>, and <style precedence> elements that React hoists into <head>. One client counter. | 4819 (-0.5%) |
huge-inline-svgHuge inline SVG A server-rendered inline <svg> with `size` rects and `size` paths derived from their index, plus one client counter. | 57115 (-49.8%) |
i18n-text-heavyLong multilingual text Many paragraphs of CJK, Arabic (RTL), emoji, accented Latin and characters that must be escaped (& < > "), with HTML entities in literal text. | 6347 (-43.2%) |
marketing-style-jsonldMarketing page with inline CSS and JSON-LD Large inline <style> and JSON-LD <script> emitted by a server component, plus static chrome and one client counter. | 35574 (-49.2%) |
product-detail-reviewsProduct detail with reviews, specs and JSON-LD Product detail page with a JSON-LD script rendered directly by the server component, a specs table, and a list of size reviews; one client add-to-cart counter. | 8527 (-48.3%) |
tiny-staticTiny static page A few host elements, no client components, no styles. | 419 (-18.2%) |
whitespace-entities-textWhitespace and entity text edge cases Text edge cases in pure server markup: leading/trailing/multiple spaces, newlines in <pre>, adjacent text expressions, empty strings, entities, raw JS-string special characters, emoji/CJK, and long unbroken words. One client counter. | 11060 (-50.5%) |
wide-treeWide flat list A single <ul> with `size` plain-text siblings, followed by one client counter. | 26695 (-79.4%) |
composition
| Example | gzip vs stock |
|---|---|
client-composition-slotClient component with server-rendered children A client Panel (toggle state) receives a server-rendered list as children and as a named slot prop. | 1236 (-59.0%) |
dashboard-client-widgetsDashboard with client chart widgets Mostly-server dashboard shell (header, sidebar, stat cards) with many small client widgets rendering inline SVG from numeric props. size = widgets. | 3588 (-9.5%) |
deep-treeDeeply nested tree A chain of `size` nested divs, one paragraph at each level, with a single client counter at the deepest level. | 2946 (-6.4%) |
islands-many-tinyLong article with a tiny island after every paragraph Long static article where each paragraph is followed by a tiny client click-counter island. size = paragraphs. | 12560 (-14.6%) |
keyed-list-client-reorderClient list reverses/filters keyed server <li> children Server passes keyed static <li> elements as children to a client list that can reverse and filter them. | 616 (-2.4%) |
modal-children-toggleClient modal with server children, closed by default A client modal is closed by default (children never rendered during SSR) and opened by click, mounting server-rendered static children. | 538 (-4.3%) |
realistic-storefrontRealistic storefront with leaf client widgets Combined storefront page: header and nav, hero carousel, product grid, reviews, footer, inline <style> and JSON-LD, with three small client widgets (cart counter, newsletter form, carousel index) placed as leaves among server markup. | 10354 (-15.5%) |
server-element-via-propsServer elements passed through several non-children props A client Layout renders server elements from header/footer/sidebar/slots props immediately (no toggling). | 788 (+0.3%) |
tabs-panels-propClient Tabs with server elements in a panels prop A client Tabs receives server-rendered static elements in a non-children `panels` array and switches which one is shown. | 627 (+1.5%) |
client
| Example | gzip vs stock |
|---|---|
all-clientEverything is a client component The server root only renders one client component that renders the whole document; nothing to elide. | 1010 (+0.6%) |
client-only-wrapperClientOnly wrapper mounting static server children after an effect A client ClientOnly renders nothing on the first render and mounts its server-provided static children after an effect. | 541 (-3.2%) |
nested-layouts-contextNested client layouts with context Three nested client layouts each provide a context (theme, locale, user) and render server-passed children; a client leaf reads all three and toggles its theme label. | 3133 (-40.9%) |
repeated-object-propsRepeated object props One shared 30-key config object is passed as the same prop to `size` client components, each rendering two of its fields. | 2812 (-0.2%) |
server-action-formForm with useActionState and a client-side action No server-function infrastructure in the fixture: a client form with useActionState and a client-side action, static server markup as children. | 849 (-3.2%) |
server-error-in-suspenseClient component throws only during SSR inside Suspense A client component throws only when typeof window is undefined, inside <Suspense> next to static server siblings; React recovers by client-rendering that boundary. | 623 (-1.1%) |
ecommerce
| Example | gzip vs stock |
|---|---|
cart-quantity-steppersCart with quantity steppers Ecommerce cart: every line item is server markup with one client quantity stepper as a leaf. size = line items. | 5810 (-18.4%) |
recursion
| Example | gzip vs stock |
|---|---|
comments-thread-recursiveDeeply nested recursive comment thread Pure server comment thread (avatar, author, text, nested replies up to depth 8) laid out as a binary tree; one client collapse-all label button. size = total comments. | 9843 (-28.6%) |
streaming
| Example | gzip vs stock |
|---|---|
nested-suspense-waterfallThree nested async server components with staggered delays Nested Suspense waterfall (20/30/40 ms), static content at each level, client counter at the leaf. | 1247 (+0.6%) |
suspense-fallback-shown-lateSuspense fallback shown only after hydration A client component suspends on user action (use() of a promise created on click, outside a transition) inside a server-rendered <Suspense fallback={static markup}>. | 652 (+0.3%) |
suspense-many-boundariesMany independent Suspense boundaries size async server components, each in its own Suspense boundary, delay 5 + (index * 7) % 60 ms, static content inside. | 5149 (-8.0%) |
suspense-staggeredSuspense streaming with staggered boundaries size async server components, each in its own Suspense boundary, resolving after 5ms + 3ms * index. | 1981 (-6.4%) |
table
| Example | gzip vs stock |
|---|---|
data-grid-client-cells20-column data grid, all client cells Same wide grid, but every cell is a client component receiving its value as a prop (worst case for elision). size = rows. | 69570 (-0.1%) |
data-grid-server-cells20-column data grid, all server cells Wide data grid where every cell is pure server markup; one client selected-row button sits outside the grid. size = rows. | 92306 (-59.4%) |
product-listingServer-rendered product grid Grid of product cards rendered entirely by server components (pure host markup), with one client sort-label button outside the grid. | 54730 (-1.9%) |
repeated-long-stringsRepeated long strings A table where every row repeats the same three 232-character strings as text and passes one of them as a prop to a client component. | 13196 (+12.4%) |
server-table-client-rowsServer-rendered table with a few client rows size rows rendered by server components; every 10th row (smoke) / 100th (full) holds a client counter. | 21261 (-49.7%) |