How We Built Hydrogen: A React Framework for Building Custom Storefronts

We’ve been building Hydrogen, a React framework for building custom storefronts on Shopify, for more than a year. We invested in cutting-edge new technology, like React Server Components, to ensure building on the Shopify platform is a terrific experience for developers. 

Here’s a look behind the scenes at how we did it, what we learned from making big bets, and what it was like building a new framework from the ground up on experimental technology.

Why We Built Hydrogen

We built Hydrogen to solve a problem merchants—especially larger merchants—were facing: How do I use Shopify’s storefront API to build a custom storefront without losing everything I get for free with a Liquid storefront? While merchants would get the benefit of a completely custom storefront, they would also face problems like picking a framework, building primitives for cart and product selection, performance, testing, accessibility, hosting, scaling, observability, and analytics.

Before Hydrogen, merchants were left with other framework options that required them to start with bulky templates or build something entirely from scratch. We believe commerce is inherently dynamic, and we found that JAMStack-oriented frameworks, which build static versions of sites and require constant re-builds, weren’t the best fit for Shopify. That’s why we went all in on building Hydrogen, our dynamic custom storefronts framework powered by React. Hydrogen aims to solve all of these problems for merchants by default, speeding up development time. Developers should enjoy building on a platform, and Hydrogen is built with the best developer experience in mind.

Iterating and Shipping and Iterating

On April 1, 2021, we created a new empty GitHub repository called Shopify/hydrogen. We didn’t know what we were building yet and definitely didn’t know what the end result would be. Since then, we’ve shipped more than 1,200 pull requests and 3,000 commits to arrive at the version of Hydrogen you see today.

We learned a lot through trial and error. We thought new components would be useful and then removed them when we found out that they weren’t. We introduced new features, but also introduced new bugs. We got critical feedback from early adopters and merchants who were using Hydrogen and addressed that feedback.

Hello, Components. Goodbye, Components.

One example of this iteration is our original list of components, including Product and SelectedVariant.AddToCartButton. We originally thought that developers would like to wrap their product detail pages with a Product component and have access to “magical” child components like SelectedVariant.AddToCartButton, which reach into React context to interact with components around them.

We quickly got feedback that the components were too rigid: they expected a certain GraphQL payload so developers weren’t able to modify the query or include custom data from third-party sources. Since this is a common requirement for custom storefronts, many of the components we built were more neat than actually useful.

We ended up removing many of these components. The result was a more trim and buttoned-down framework, offering primitives truly useful to improving developer experience.

To Fetch or Not to Fetch (in React)

Another cycle we worked through is how data fetching works. Early in Hydrogen’s development, we decided to use server components and React 18’s streaming server-side rendering (SSR). This meant we needed to have a corresponding data fetching story on the server.

Other frameworks offer top-level route data fetching utilities, like loader() or getStaticProps(). Instead of inventing yet another bespoke loader API, we built component-level data fetching into Hydrogen.

There are drawbacks to component-level data fetching when implemented naively. For example, it’s easy to invoke network request waterfalls if you request data in nested components because each subsequent component is blocked from rendering until the previous one finishes.

To solve for these drawbacks, we built a preload cache to hoist nested data queries to execute in parallel. When enabled, preloaded queries start running the instant the page is requested for subsequent requests rather than in a waterfall pattern.

We want developers to feel empowered when using this feature, so we also built an experimental query timing logger to alert them when a network request waterfall is detected on the page. This provides a hint to enable preloading for a query. This is still an area we’re actively exploring. We anticipate the React ecosystem to move in this direction eventually, and we’re excited to see what new solutions emerge.

Breaking Up with Fragments

In the early days of the Hydrogen developer preview, we exported strings containing GraphQL fragments from several UI components. Our intention was to make it easy to use the UI components by plugging in the data required to render them as fragments to the query on the server.

Over time, we added more functionality to these UI components, meaning more data was required to make them work correctly. This led to larger and more complex fragments, including the use of required variables, large default pagination values, and lots of nodes for metafields and variants. 

We quickly discovered that the fragments had become bloated. With each new Hydrogen app created, developers were fetching so much unnecessary data that their pages were loading more slowly than if they had written the queries from scratch. To make matters worse, the contents of the fragments themselves were obscured away in the Hydrogen npm package, making it difficult to discover what data was being requested. To mitigate this issue, we did two things that improved the developer experience of Hydrogen. 

First, we created a new experimental logger to detect unused properties in GraphQL queries. It alerts developers when they’re requesting data that isn’t being used in their components and encourages them to remove those fields from the pages to improve performance. 

A mockup of Hydrogen's developer interface, showing the DevTools section on the right-hand side.
Hydrogen’s DevTools offer helpful tips to developers for improving the speed and performance of their custom storefront.

Second, we removed almost every fragment export from Hydrogen’s npm package. This meant more verbose queries in newly-scaffolded Hydrogen apps, but it also made the queried data more discoverable by the developers. It encouraged developers to fine-tune the queries for their needs instead of relying on fragments.

The results were enormous: some routes in the Hydrogen demo store template saw their load time cut in half . We’re still experimenting with these tools, as well as finding a way to introduce fragments in a smarter way to help developers new to GraphQL get onboarded more quickly.

Building in Public

One benefit of building something like Hydrogen as a public open-source project is contributions from external developers. Examples include adding a new option to useShopQuery, preventing errors in the demo store template, and adding internationalization support. In fact, Hydrogen has accepted contributions from more than a dozen external developers since November 2021!

Another benefit of building in public is that we get external feedback from developers via discussions and issues. We proposed and got feedback on our initial routing strategy and aligned on our caching API. We also heard that developers really wanted us to use TypeScript in our demo store templates (so we did!).

Making Big Bets

We took risks when we set out to build Hydrogen in early 2021. We believe the technology we use will continue to improve over time and plan to keep learning from our previous bets to make better ones in the future. Here are a few highlights of the risks we took, and how they turned Hydrogen into what it is today.

On React Server Components

In December 2020, React announced React server components (RSC), a still-in-development feature that would enable React components to be rendered on the server, with zero client-side JavaScript needed, resulting in faster page rendering. Fast forward to April 2021, when we were figuring out what data fetching API to build into Hydrogen.

In our Slack channel, we lamented that the RSC pattern was ideal, and it was unfortunate it wasn’t released yet.

So instead of waiting, we decided to build a version that worked in Vite and started using it immediately. We naively implemented (based on some reverse engineering) the RSC payload that serializes React components and makes them interactive in the browser.

A screenshot of a pull request in Github by this post's author, Josh Larson. It's titled "Switch to React Server Components".
A screenshot of the PR to switch to server components in our private Hydrogen repository in May 2021 before we switched to a public repository with the developer preview launch in November 2021.

This naive implementation allowed us to push forward building a framework based on server components and support things like component-level data fetching, instead of waiting for months or years for a final production version of server components to come to fruition.

We eventually switched to an official version of server components powered by React’s RSC payload.

RSC is the biggest technical bet we’re making on Hydrogen today. We believe it provides the perfect separation of concerns between client and server components. The ability to mix and match server components from first-party and third-party codebases creates a type of modularity that’s unmatched in any other framework. It’s impossible to take a component which fetches data on the server and drop it into a non-RSC framework, because other frameworks require data fetching to happen at the routing level. With RSC, server components can be added to Hydrogen apps and work out of the box. Third-party developers are the lifeblood of the Shopify ecosystem, so this makes perfect sense for Hydrogen.

A mockup of the Hydrogen developer interfaced, showing an example prompt that it provides to help developers use React Server Components. The prompt reads "Do not use useState in React Server Components. These components only run once and therefore cannot handle state like traditional client components."
Hydrogen provides tools to help developers use React Server Components to power their custom storefronts.

We’re working closely with the React core team at Meta and the Next.js team at Vercel to iterate on and refine the design of server components. We’ve given feedback about the server module conventions proposed in server components, and we’re working to find an API that works best for all developers. Hydrogen and Next.js will stay up to date with the specification of the server components API as we work out the last details.

We also recognize that education is key for adoption, so we’ve written documentation about server components in the context of Hydrogen. We’ll continue to improve our documentation to help the community adopt server components in the coming months.

On Vite

Until a year ago, webpack reigned supreme as the bundler of choice for most front-end projects. It’s a fine tool, but we decided on Vite as our bundler for Hydrogen.

Building on Vite was somewhat risky because SSR support is still considered beta. However, we’ve found that Vite is incredibly fast, easy to extend with custom options, and quite popular among developers. We continue to collaborate with the Vite core team through PRs and bugfixes. Shopify is also a financial sponsor of the Vite and Vue.js projects.

Read more about how we use Vite to build a best-in-class developer experience for Hydrogen.

On Tailwind

In the original Hydrogen demo during Unite 2021, Tobi used Tailwind CSS to build an interactive product detail page with 3D models. Tailwind makes it easy to apply atomic CSS classes to modify a component’s appearance without having to jump back and forth between a React component and a CSS file.

Tailwind is incredibly popular—in the State of CSS 2021 survey results, it ranked at 78% in terms of developer satisfaction—and picking it for our demo store template wasn’t a huge bet on its own. But atomic CSS is a controversial approach to styling web applications, and we knew not everyone would want to use Tailwind in Hydrogen apps. However, we’ve found it to be incredibly effective and performant for building and scaling large Hydrogen storefronts.

Read more about why Tailwind is the perfect fit for building Hydrogen apps.

Collecting and Implementing Feedback

Projects like Hydrogen and Oxygen aren’t built in a silo. This is especially true when building an open-source project like Hydrogen that interacts with many other open-source projects and is deployed to many different surfaces.

Collaborating Internally

Shopify is a big company, and we couldn’t have built Hydrogen without collaborating with fellow Shopifolk working in different areas of the company.

One of the ways we collaborate internally is with our API team that constantly improves the Storefront API based on feedback from the community. This includes a new Cart API, an overhauled schema, and a new private API token to improve rate limiting on server-to-server calls.

We also frequently work with the Oxygen team to fine-tune the deployment pipeline for Hydrogen projects. This includes working with the team to define requirements for the Oxygen runtime, like providing access to geolocation properties via incoming requests.

The Hydrogen team is working with other core Shopify teams to add features to the framework, including customer authentication, search engine optimization (SEO), and analytics. We’re also creating example code to demonstrate how to integrate with these Shopify features.

Dogfooding Hydrogen internally is an important part of building the framework too. Other teams at Shopify have been using Hydrogen in their own products, like Linkpop and Shopify Supply. Putting Hydrogen into production scenarios helped us uncover limitations of our APIs, leading to improvements.

Collaborating with the Industry

Building Hydrogen led to way more collaboration outside of Shopify than I ever expected. 

We meet with the React core team on a regular basis to discuss our exploration into server components. We’ve contributed pull requests for new features, bug reports, and bug fixes.

We’re continuing to iterate on a Vite version of the server components plugin upstream.

We’re working with Vercel to define new server components conventions in alignment with Next.js and determine the best way to deploy Hydrogen apps on Vercel.

We’ve collaborated extensively with the Vite core team with PRs to add new features and fix bugs.

Early on, we partnered with Cloudflare on Oxygen and worked with them to refine their ReadableStream implementation. We also joined WinterCG, and have already proposed topics like standardized access to cookie headers and runtime variables.

Google has an initiative called Aurora. We’re working with them to build a standardized Image component and enforce best practices in Hydrogen from the start.

Early third-party adopters of Hydrogen like Sanity helped us define early API patterns and created examples like the Hydrogen Sanity demo store.

We’re working with Remix to create a Hydrogen Stack and find a good way to implement Hydrogen components in a Remix app.

We’re working with other hosting platforms like Netlify to deploy Hydrogen outside of Oxygen.

Developers on the Hydrogen team have also been busy presenting and speaking at conferences. Helen Lin spoke at ReactConf in December 2021, and I spoke on the FSJam podcast earlier this year. Bret Little spoke with Ryan Carniato about server components on a livestream, and Anthony Frehner spoke at RemixConf 2022. Matt Seccafien and Cathryn Griffiths led a workshop at React Advanced London in 2021 while Scott Dixon taught developers about Hydrogen and server components at a SmashingConf workshop.

Keep an eye out for even more appearances from the Hydrogen team on podcasts and at conferences!

The Future of Hydrogen

Today, Hydrogen is used in production at Allbirds, Shopify Supply, Shopify Hardware, and Shopify itself. The Hydrogen npm package is downloaded more than 70,000 times each month. 

The future is bright and we’re just getting started. Expect to see lots of updates to Hydrogen, including deeper integration with the rest of the Shopify ecosystem, a new design for server components, a new Hydrogen router, and ways to incrementally adopt Hydrogen from an existing Liquid storefront.

If you haven’t already, give Hydrogen a spin at https://hydrogen.new or check out the amazing documentation. We’re excited to see developers use Hydrogen to make commerce great for everyone!

Josh Larson is a Senior Staff Developer at Shopify working on the Hydrogen team. He works remotely from Des Moines, Iowa. Outside of work, he enjoys spending time with his family, playing music with friends, and watching a good television show.

Learn More About Hydrogen

If building systems from the ground up to solve real-world problems interests you, our Engineering blog has stories about other challenges we have encountered. Visit our Engineering career page to find out about our open positions. Join our remote team and work (almost) anywhere. Learn about how we’re hiring to design the future together—a future that is digital by design.