Rapid Development with Hydrogen: Building a Product Page

Last year we released Hydrogen, our React-based framework for building custom storefronts. Hydrogen allows developers to build fast, dynamic commerce experiences by leveraging streaming server-side rendering, React Server Components, and caching APIs. Hydrogen is currently in developer preview and I'm excited to show you how you can rapidly build out a simple product page by leaning on Hydrogen's components.

Sample Snowdevil Product Display Page showing an image of a snowboard, the name, price, variant picker, and Add to cart button
We’ll be using Hydrogen to build a product display page.

Previously, constructing a custom storefront required developers to manually manipulate data and create custom components for each page. Hydrogen accelerates this process by offering Shopify-specific commerce components, hooks, and utilities that allows developers to focus on building unique storefront experiences.

Getting Started

To get started, generate a starter app by heading to hydrogen.new.

Most of the files you’ll work with are located in the /src directory. This directory contains a set of boilerplate components and pages, the main app component (App.server.jsx), and the client/server entry points. For an in-depth overview, see the getting started guide.

The starter app is connected to a demo store which contains a few products. You can find the details in /shopify.config.js.

Hydrogen connects the Storefront API (with the access token already configured) and allows us to fetch data from descending server components using the useShopQuery hook.

Hydrogen's starter app comes with a convenient out-of-the-box product page. To get hands-on with the components and understand the moving parts, we'll put this page aside and build our own.

Our goal is to set up a Product Context that allows us to render product details with ease:

From there, we'll create a variant picker and allow the customer to add the product to cart.

Creating a Product Page

In your app, create a new page /src/pages/featured.server.jsx with the following code:

Hydrogen's file based routing kicks in, and a new route is created at /featured.  This code will be rendered on the server because the filename ends in .server.jsx. Our page is wrapped in the starter app's Layout component that has a cart ready to go.

Sample Snowdevil Product Display Page that's missing the image of a snowboard, name, price, variant picker, and Add to cart button
An empty product page wrapped in a Layout component.

We'll be using a ProductProvider component (alias: Product) to set up a context with product details. This allows descendents to use components like Product.title (rendering the product title) and hooks like useProductOptions (keeping track of the selected variant). Our product page requires client-side state, so we'll create a /src/components/FeaturedProductDetails.client.jsx client component to house product details.

You'll notice ProductProvider requires a Product object to be passed through as a prop. Instead of manually writing a query to fetch all of the required fields, many Hydrogen components (including this one) have GraphQL fragments that you can use instead. It's important to note that this fragment includes variables that you’ll need to provide values for when performing the query. Joining these pieces together, the /src/pages/featured.server.jsx file looks like this:

And the /src/components/FeaturedProductDetails.client.jsx file looks like this:

The featured product page now shows a product title wrapped in the starter app layout.

Sample Snowdevil Product Display Page showing the title but still missing the an image of a snowboard, price, variant picker, and Add to cart button
The product title appears on the product page.

With the context in place, it's a breeze to build out the rest of the page—try adding a product description, price, or custom component like handle (hint: you'll need to import Hydrogen's useProduct hook!):

Customizing Components

Hydrogen components are customized using passthrough and render props. Using passthrough props, you pass attributes as props to the component that passes them through to the rendered HTML tag:

<Product.Price className="font-bold" aria-live="polite" />

And the output is:

<div class="font-bold" aria-live="polite">$749.95</div>

Using render props, you pass a function that returns JSX as a child to the Hydrogen component:

<Product.Price>
  {({ amount, currencySymbol }) => `Fancy price: ${currencySymbol}${amount}`}
</Product.Price>

And the output is:

<div>Fancy price: $749.95</div>

Adding a Variant Picker

Merchants add variants to products that come in more than one option, such as color. To accommodate these, we'll need a variant picker dropdown. We can use the useProduct hook to retrieve a list of variants and get/set for the selected variant. A simple dropdown is created by mapping the variants. When a selection is made, setSelectedVariant updates the state:

To set the initial variant, pass the product provider an initialVariantId property. This is a good place to use the flattenConnection utility that transforms a connection object from the Storefront API into a flat array of nodes:

A close-up of the Variant Picker showing three options a user can select
The variant picker.

Adding the Add to Cart Button

The Product.SelectedVariant.AddToCartButton component knows which variant is selected and takes care of adding the item to cart. We can expand on this further by toggling a disabled state and changing the text based on a variant's availability:

Finishing Touches

With a functioning product page in place, we now add a variant image (you guessed it, there’s a component for that too) and tidy up the styling:

The final code is found on StackBlitz.

Sample Snowdevil Product Display Page showing an image of a snowboard, the name, price, variant picker, and Add to cart button
The final product

Hydrogen Enables Rapid Development

Taking advantage of these components, hooks, utilities, and fragments allows you to skip many of the repetitive and mundane parts of building a custom storefront, speeding up the development process.

For more code examples, be sure to examine the starter template which provides a full purchase journey out-of-the-box. You can find these files in the /src directory of your project.

This post just scratched the surface, check out all of the components and take Hydrogen for a spin on your next project!

Scott’s a Developer Advocate at Shopify, located on the east coast of Australia and formerly a Shopify app developer and developer bootcamp coach. When he's not tinkering with code, you'll find him checking the surf or hanging out with his family.


Wherever you are, your next journey starts here! 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. Intrigued? Visit our Engineering career page to find out about our open positions and learn about Digital by Default.