Streaming server-side rendering (SSR)
You can improve your app's loading performance by rendering components on the server and streaming them to the client.
This guide describes how streaming server-side rendering (SSR) works in a Hydrogen app. It also explains how to use Suspense to manage asynchronous operations in your app.
How streaming SSR worksAnchor link to section titled "How streaming SSR works"
Streaming SSR is a feature in React that allows you to load data over a network in multiple chunks. The chunks are loaded out of order in parallel to rendering, which makes your Hydrogen storefront fast and performant.
The following clip shows an example of streaming content. The yellow boxes represent the content to display. As the streaming approaches 1.5 seconds, each yellow box gets replaced by a blue box at the specified time. The blue boxes represent that the data is ready:
The following example shows the source code for a streamed document. The HTML that's being streamed in chunks is displayed out of order:
Benefits of streaming SSRAnchor link to section titled "Benefits of streaming SSR"
Streaming SSR provides the following performance benefits:
- Fast TTFB (Time to First Byte): The browser streams the HTML page shell without blocking the server-side data fetch.
- Progressive hydration: As server-side data fetches are resolved, the data is streamed within the HTML response. The React runtime progressively hydrates the state of each component, all without extra client round trips or blocking on rendering the full component tree.
You have a product page that contains a lot of buyer personalized content:
- A localized description and price for a given product
- A dynamic list of recommended products powered by purchase and navigation history
- A custom call to action or promotion banner
The following table describes different strategies for implementing the product page, and the benefits of using a streaming SSR strategy:
|Client-side strategy||Server-side strategy||✅ Streaming SSR strategy|
A client-side strategy might result in a fast render of an empty product page skeleton, with a series of post-render, browser-initiated fetches to retrieve and render the required content.
However, client-initiated roundtrips usually result in a subpar user experience.
A server-side strategy might fetch the data on the server and return it in the response.
However, server-side rendering offers a slow TTFB because the server is blocked on the data.
With a streaming SSR strategy in Hydrogen, you can stream, progressively hydrate, and render the product page to load content fast and efficiently.
Streaming SSR contrasts with standard SSR, where TTFB is blocked until all data queries are resolved. Individual components can also show custom loading states as the page is streamed and constructed by the browser.
Using SuspenseAnchor link to section titled "Using Suspense"
React 18 introduced Suspense for data fetching to complement streaming SSR. Suspense is a feature of React that governs the appearance and behavior of placeholder content inside components while asynchronous data-fetching is in progress.
How Suspense worksAnchor link to section titled "How Suspense works"
Suspense establishes where you need to await an initiated data fetch. This means that Suspense doesn't define where you initiate fetching data. Instead, it specifies where you access the results of fetching data.
Suspense is implemented as a React component that wraps other components, so that each pair of Suspense tags corresponds to a Suspense boundary.
A Suspense boundary is any portion of React component code that's enclosed by a pair of Suspense component tags. The components in a Suspense boundary share common rendering behaviors while awaiting data controlled by Suspense. Multiple Suspense boundaries can co-exist within a single component, and Suspense boundaries can also be nested.
Example: Layout fallbackAnchor link to section titled "Example: Layout fallback"
Suspense lets you render a fallback while a component is waiting for an asynchronous operation to finish. The following example shows how you can use
Suspense to add a layout fallback to improve cumulative layout shift (CLS):
Example: No Suspense component definedAnchor link to section titled "Example: No Suspense component defined"
If you don't define a
Suspense component, then React waits for the streaming to finish before showing the final layout. The following clip shows a streamed document source that doesn't include a
Example: Suspense component definedAnchor link to section titled "Example: Suspense component defined"
Suspense component is wrapped around a group of components that fetch data, it waits for the last component in the group to resolve before rendering. The order of the streamed content doesn't change in the streamed document source: