At the time of writing, the newest version of Gatsby, v5, ships with partial hydration as a feature. Still in beta, partial hydration enables you to selectively add interactivity to your otherwise completely static app, resulting in improved frontend performance while still keeping the benefits of client-side apps.
In React-based apps and websites, all JavaScript code must be downloaded before the page becomes interactive, affecting the Time to Interactive (TTI) metric in particular. But, thanks to Gatsby’s partial hydration, developers can now hydrate only the necessary JavaScript code for React components, thereby reducing the JavaScript bundle size and increasing page speed.
In React, hydration is the process of using client-side JavaScript to add application state and interactivity to server-rendered HTML. Gatsby implements partial hydration by leveraging React server components to generate the server components’ output, starting from the page level down to isolated components. In this article, we’ll explore partial hydration in a Gatsby and React application.
Most Gatsby sites are content-based, requiring only a few sections to be interactive. However, to ensure that click events, effects, and state changes work correctly, we have to download the JavaScript that handles these events. Unfortunately, this behavior can result in a lot of unused JavaScript code being downloaded, making your website slower and more expensive. Here, partial hydration comes in handy.
According to the Gatsby docs, partial hydration is useful for pages with interactive components where some components need interactivity on a page and some can be statically rendered, for example, a product page with a lot of static content and a few interactive forms and buttons.
Take a look at the following diagram from the Gatsby site. It explains the differences between partial hydration and full hydration.
In this fictional page, the components are mostly static, except for the carousel, which is an interactive component. Typically, when this page is loaded, we request JavaScript code for parts of the site that are static and don’t need it. However, with partial hydration, we request only JavaScript code for the interactive components:
Interactive components are components that contain useEffect
, useState
, createContext
, or event handlers. Class components are not compatible with server components and should be marked as interactive as well.
Let’s run through an example and learn how to use partial hydration. To get started, install the latest version of Gatsby and experimental versions of React and react-dom
:
npm install gatsby@next react@experimental react-dom@experimental
Next, go ahead and enable the PARTIAL_HYDRATION
flag in the gatsby-config
file:
//gatsby.config.js flags: { PARTIAL_HYDRATION: true, },
By default, Gatsby 5 treats every component as a server component, starting from the top-level pages and generating React Server Components (RSC) files for each page. So, to tell Gatsby to enable partial hydration for a component, you have to add the "use client"
directive, an RSC convention, to the top of the component:
// Component.js "use client"; export function MyInteractiveComponent() { const [myState, setState] = useState(null); useEffect(() => { setTimeout(() => setState(‘interactive’) }, 3000) return <div>{myState}</div> }
With the "use client"
directive, the component will become a special reference object. This object can’t be accessed directly in that file, but it can be passed into React as if it were a plain component. React will then know to send that reference object to the client, which will then be rendered as a client component on the server.
You can see a full example of implementing partial hydration in the Partial Hydration Starter GitHub repository. If you navigate to the src/components/demo.js
file, as shown below, you can see a quick example of a client being partially hydrated:
/** * To mark a component as client side, you add the "use client" directive. * @see {@link https://github.com/reactjs/rfcs/blob/serverconventions-rfc2/text/0000-server-module-conventions.md} */ "use client" import React, { useCallback, useState } from "react" export function Demo() { const [counter, setCounter] = useState(0) const onClick = useCallback(() => { setCounter(counter => counter + 1) }, []) return ( <div style={{ marginTop: "10px", marginBottom: "10px" }}> <p style={{ margin: 0 }}>Current counter: {counter}</p> <button onClick={onClick}>Add counter</button> </div> ) }
In the code block above, we have a simple component with a button that will increase the counter using useState
. This component is interactive because of its use of useState
. This component is imported and utilized in the src/pages/using-partial-hydration.js
file, as seen below:
import * as React from "react" import { Link } from "gatsby" import Layout from "../components/layout" import Seo from "../components/seo" import { Demo } from "../components/demo" function usingPartialHydration() { return ( <Layout> <h1> Gatsby supports <b>Partial Hydration</b> </h1> <p> You can now mark components as client side. This will reduce Javascript shipped to the user. </p> <p> The component below is such a component, if you check the Network Tab after a "gatsby build". You will see that we only load the component code and non of the layout </p> {/* Usage of the client component */} <Demo /> <p> Checkout <a href="https://gatsby.dev/v5-partial-hydration">the RFC</a>{" "} to learn more. </p> <Link to="/">Go back to the homepage</Link> </Layout> ) } export const Head = () => <Seo title="Using TypeScript" /> export default usingPartialHydration
This page
component is mostly comprised of a content-filled page, except for the Demo
component. When Gatsby tries to build this page, it will generate an RSC file for the page. Instead of fetching page
component JavaScript files in the browser, it requests a page-data-rsc.json
file. The JSON file is a description of the UI, and any client components are included as a bundle reference to get the actual code of the component.
Right now, partial hydration only works when you build for production, i.e., gatsby build
or gatsby serve
, and not gatsby develop
. When you run gatsby build
to build for production, you should see the following lines in the output of the command, which signify that the client components were partially hydrated during the production build:
success Building Partial Hydration renderer - 0.530s ... success Building partial HTML for pages - 0.040s - 6/6 149.35/s
At the time of writing, partial hydration in Gatsby is still in beta. Let’s review some of the currently known issues you might encounter with partial hydration.
Styling libraries like emotion
and styled-components
do not currently work when partial hydration is enabled. At the time of writing, React Server Components is not supported.
gatsby-plugin-offline
The gatsby-plugin-offline
is used to make a Gatsby site work offline and more resistant to bad network connections. In partial hydration’s beta stage, gatsby-plugin-offline
is not supported, and there have been reports of Gatsby sites failing to build.
Setting up partial hydration for your Gatsby site will lead to a good user experience and significantly increase page load speed. The result of shipping less JavaScript code to the client will directly impact your performance scores, most notably the Time to Interactive measurement.
It’s important to keep in mind that partial hydration is still in beta. So, use this in production at your own risk, as there might be some future breaking changes.
To learn more about partial hydration with Gatsby 5, check out the RFC and the conceptual guide. I hope you enjoyed this tutorial, and happy coding!
Install LogRocket via npm or script tag. LogRocket.init()
must be called client-side, not
server-side
$ npm i --save logrocket // Code: import LogRocket from 'logrocket'; LogRocket.init('app/id');
// Add to your HTML: <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script> <script>window.LogRocket && window.LogRocket.init('app/id');</script>
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowBuild scalable admin dashboards with Filament and Laravel using Form Builder, Notifications, and Actions for clean, interactive panels.
Break down the parts of a URL and explore APIs for working with them in JavaScript, parsing them, building query strings, checking their validity, etc.
In this guide, explore lazy loading and error loading as two techniques for fetching data in React apps.
Deno is a popular JavaScript runtime, and it recently launched version 2.0 with several new features, bug fixes, and improvements […]