Integrating a headless CMS with Shopify Hydrogen

You can easily integrate Hydrogen with a headless CMS and we’ll show you how with Hygraph.

Mdu Sibisi
Mdu Sibisi
Integrating a headless content platform with Shopify Hydrogen

You might want to build your online storefront using a headless CMS for several reasons. You might wish to greater customization of your website, faster server-side rendering, smart caching, or progressive hydration.

Built on top of Remix, Shopify's full-stack web framework, Hydrogen, is a headless framework that allows you to develop and deliver custom storefronts for your Shopify site. You can easily integrate Hydrogen with a headless CMS, and we’ll show you how with Hygraph.

What is Shopify Hydrogen?Anchor

Before jumping into the step-by-step tutorial, let's back up and explain what Shopify Hydrogen is.

You already know Hydrogen is a headless framework. By default, it’s designed to work with Remix, but it provides a React library that’s portable to other supporting frameworks. Hydrogen's integrated storefront API client lets you accelerate your eCommerce development with pre-built components, utilities, and hooks.

Some of the key benefits of Hydrogen include:

  • Customization: Build a highly customized Shopify storefront without relying on pre-built themes or templates. Complete control over the design and functionality of your website means you can create a unique user experience for your brand.
  • Fast server-side rendering: Hydrogen renders pages on the server side, which can significantly reduce page load times. That’s especially important for eCommerce sites, where slow loading can mean frustrated customers or lost sales.
  • Improved SEO: Server-side rendering improves SEO by making it easier for search engines to crawl and index your site. This means your site is more likely to appear at the top of search engine results pages, which can drive traffic and increase sales.
  • Greater flexibility: Hydrogen offers more control over your storefront than traditional Shopify themes or templates, so you can easily customize your site to meet the unique needs of your business. You can also make changes quickly and easily as your business evolves.

Building a headless content platform with Shopify Hydrogen and HygraphAnchor

To follow along with this tutorial, you need to have the following lined up:

  • A Hygraph account and a Shopify account.
  • A Hygraph project named Hygraph Shopify Store with a model named Product.
  • A Shopify store named Hygraph-Hydrogen Store with at least two products. You can add any two products, but if you want to use the same data as this tutorial, you can add these.
  • Node.js v16 or later and npm v7 or later installed on your local machine.
  • A web browser and code editor.

Installing Shopify for HygraphAnchor

On your Hygraph project dashboard, select Apps from the sidebar and click Go to Marketplace.

Shopify apps

From the Hygraph marketplace, select Shopify.

Hygraph marketplace

On the Shopify app description page, click Install Shopify for Hygraph.

Shopify app description page

On the app installation page, select your project and environment and click Install app.

App installation page

On the app permission authorization page, click Authorize app.

App permission authorization page

On the Shopify configuration page, fill out the store token and the store domain and click Save. You can find more information about how to access these details from the helper links provided on the page. Take note of the values—you’ll use them later on.

Shopify app configuration page

Click Schema in the sidebar and ensure the *Product model is selected.

The schema builder

You can now go ahead and add some fields. Select Shopify product picker as the field type, provide the display name, and click Add. With this field, you can select products from Shopify.

Shopify product picker field

Next, create a Single line text field with the display name color. This field stores the color of the product.

Creating the color field.

Your Product model page should now look like this:

product model page

Adding content to the product modelAnchor

To add some content to the model that you just created, select Content from the sidebar and click Add entry.

Content page

On the new product page, click Select product from Shopify to select a product. Make sure you also provide a color for your product.

New product page

Click Save & publish to push the product to the published stage so that it can be retrieved from the API endpoint. Do this for all your products.

Product and color provided

Your Product content page should now look like this:

Product content page

Select API playground from the sidebar and run the Products query to check whether the products you previously added can be retrieved. If the query returns some data, everything is working as expected, and you can move on to enable API access.

Running the products query

Enable API access. This guide enables full public API access, though you can secure your API with permanent auth tokens. Copy the content API endpoint—you’ll use it later.

Creating the Hydrogen storefrontAnchor

To create a new Hydrogen storefront, run the following command in the terminal:

npm create @shopify/hydrogen@latest

Choose Hello World as your template, JavaScript for the language, hydrogen-hygraph-storefront for the app location, and select Yes to install dependencies with npm.

Next, change into the hydrogen-hygraph-storefront directory and run npm run dev to confirm that the project was successfully created and all the dependencies installed. Open http://localhost:3000 on your browser, and you should see the following page:

hydrogen project homepage

You must now configure your storefront token in the .env file. Open the .env file in the project root folder and replace the PUBLIC_STOREFRONT_API_TOKEN and PUBLIC_STORE_DOMAIN values with the Shopify store credentials that you obtained when configuring the Shopify app in the Hygraph CMS.

Configuring Tailwind and creating the layoutAnchor

You need to configure Tailwind CSS to apply basic styling to the storefront. Execute the following command to install Tailwind CSS and the dependencies it relies on:

npm install -D tailwindcss postcss postcss-cli autoprefixer concurrently

Run the command npx tailwindcss init -p to generate the Tailwind CSS configuration files tailwind.config.js and postcss.config.js. Open tailwind.config.js and replace the existing code with the code below that points Tailwind to all the template files:

/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./app/**/*.{js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}

Create a file named tailwind.css in the app/styles folder and add the following Tailwind directives:

@tailwind base;
@tailwind components;
@tailwind utilities;

Open the package.json file and replace the dev and build scripts with the following scripts, which will build your development and production Tailwind CSS:

"build": "npm run build:css && shopify hydrogen build --entry ./server",
"build:css": "postcss app/styles/tailwind.css -o app/styles/tailwind-build.css --env production",
"dev": "npm run build:css && concurrently -g -r npm:dev:css \"shopify hydrogen dev\"",
"dev:css": "postcss app/styles/tailwind.css -o app/styles/tailwind-build.css -w",

To configure the page layout, create a file named Layout.jsx in the app/components folder and add the code below:

export function Layout({children, title}) {
return (
<div className="flex flex-col min-h-screen">
<header
role="banner"
className="flex items-center justify-between w-full h-16 p-6 sticky top-0 gap-4 shadow-sm"
>
<div className="flex gap-8">
<a className="font-bold" href="/">
{title}
</a>
</div>
</header>
<main
role="main"
id="mainContent"
className="flex-grow p-6"
>
{children}
</main>
</div>
);
}

The Layout.jsx file ensures the display is consistent on all pages by displaying a header at the top and rendering the other content in the main tags.

Next, open the app/root.jsx file and replace the existing code with the following:

import {
Links,
Meta,
Outlet,
Scripts,
ScrollRestoration,
useLoaderData,
} from '@remix-run/react';
import favicon from '../public/favicon.svg';
import tailwind from './styles/tailwind-build.css'
import { Layout } from './components/Layout';
export const links = () => {
return [
{rel: 'stylesheet', href: tailwind},
{
rel: 'preconnect',
href: 'https://cdn.shopify.com',
},
{
rel: 'preconnect',
href: 'https://shop.app',
},
{rel: 'icon', type: 'image/svg+xml', href: favicon},
];
};
export const meta = () => ({
charset: 'utf-8',
viewport: 'width=device-width,initial-scale=1',
});
export async function loader({context}) {
const layout = await context.storefront.query(LAYOUT_QUERY);
return {layout};
}
export default function App() {
const data = useLoaderData();
const {name: shopName} = data.layout.shop;
return (
<html lang="en">
<head>
<Meta />
<Links />
</head>
<body>
<Layout title={shopName}>
<Outlet />
</Layout>
<ScrollRestoration />
<Scripts />
</body>
</html>
);
}
const LAYOUT_QUERY = `#graphql
query layout {
shop {
name
description
}
}
`;

The code above imports the Tailwind build and wraps the entire application with the Layout component that you created previously. It also retrieves your Shopify store name and passes it to the Layout component as a prop to display as a page title.

Fetching and displaying the products.Anchor

To fetch and display all the products that you added to your Shopify store, create a file named index.js in the app/routes folder and add the code below:

import { useLoaderData } from '@remix-run/react';
import { Image } from '@shopify/hydrogen';
export const meta = () => {
return {
title: 'Hydrogen',
description: 'A custom storefront powered by Hydrogen'
};
};
// query to fetch all products from Hygraph
const getAllProductsFromHygraph = `
query Products {
products {
id
productId
color
}
}
`;
// query to fetch all products from Shopify
const getAllProductsFromShopify = `#graphql
query products($ids: [ID!]!) {
nodes(ids: $ids) {
...on Product {
id
title
handle
featuredImage {
url
width
height
}
description
priceRange {
minVariantPrice {
amount
}
}
}
}
}
`
export async function loader({ context }) {
// fetch the products from Hygraph
const response = await fetch('<your-hygraph-content-api-endpoint>', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ query: getAllProductsFromHygraph })
});
const { data } = await response.json();
// extract the product IDs from the Hygraph response and push them into an array
const productsArray = data?.products.map(prod => prod.productId)
// query the Shopify storefront for the products info and pass the array containing the product IDs
const res = await context.storefront.query(getAllProductsFromShopify, {
variables: {
ids: productsArray
}
})
// return the data fetched from Hygraph and Shopify
// products => the data fetched from Shopify
// data => the data fetched from Hygraph
return { products: res.nodes, data: data.products }
}
export default function Index() {
const { products, data } = useLoaderData();
return (
<section className="w-full gap-4">
<h2 className="font-bold text-2xl mb-4">Products</h2>
<div className="flex space-x-6">
{/* Loop through the products and display the necessary info */}
{products.map((product, i) => {
return (
<div key={product.id} className="grid gap-4">
{product?.featuredImage && (
<div className='h-64 w-96'>
<Image
alt={`Image of ${product.title}`}
data={product.featuredImage}
className='w-full h-full object-cover rounded'
/>
</div>
)}
<h2 className="whitespace-pre-wrap max-w-prose font-medium text-copy">
<span>{product.title} {' • '}</span>
<span className='text-sm text-gray-500'>{product.id === data[i].productId && `${data[i].color}`} {' • '}</span>
<span>Kshs. {product.priceRange.minVariantPrice.amount}</span>
</h2>
</div>
);
})}
</div>
</section>
);
}

The code above defines two queries: getAllProductsFromHygraph, which specifies the data to be fetched from Hygraph; and getAllProductsFromShopify, which specifies the data to be fetched from Shopify.

A Remix loader function fetches data from the Hygraph Content API endpoint using the getAllProductsFromHygraph query. It loops through the Hygraph response, extracts the productId field from each object, and pushes the values of these fields into the productsArray array.

The loader function uses Hydrogen's storefront param to query the Shopify storefront using the getAllProductsFromShopify query and passing the productsArray as a variable. This retrieves all the products whose ID is defined in the productsArray variable.

The code loops through the products array, which contains the products fetched from the Shopify storefront, and renders the necessary data. It also renders the custom field color, which you defined in the Hygraph CMS.

Editor's Note

Be sure to replace <your-hygraph-content-api-endpoint> with your own API endpoint.

Testing the final applicationAnchor

To test whether the integration is successful, execute npm run dev in your terminal to start a local development server and navigate to http://localhost:3000 on your browser. If you used the two products from this tutorial’s repo, you should see the following:

custom hydrogen storefront homepage

The rendered webpage shows the product image, title, and price fetched from Shopify. The product color was fetched from Hygraph.

In brief:

  • You fetch the IDs of the products from Hygraph.
  • You use the IDs to specify the products to fetch from your Shopify store.
  • You display all the product details by combining the data fetched from Hygraph and Shopify.

Editor's Note

Note that you can check out all the code in this article on GitHub.

ConclusionAnchor

Hydrogen gives you significantly more flexibility and customization over your Shopify storefront. Integrate it with a federated content management platform like Hygraph, and you’ve got a headless content platform that can evolve with your eCommerce application as quickly as your users need you to.