Shopify Hydrogen is a React-based framework designed for building custom, high-performance ecommerce storefronts. It leverages React Server Components (RSCs) and Vite to deliver fast-rendering experiences, particularly for mobile-first brands requiring low-latency interactions.

Hydrogen integrates tightly with Shopify’s Storefront API to allow server-side data fetching and edge rendering via Oxygen, Shopify’s global hosting solution. The framework prioritizes reducing time-to-interactive (TTI) and optimizing core web vitals, making it suitable for media-rich storefronts where video content plays a central role.

Hydrogen Architecture

Hydrogen projects follow a structured approach to server-side rendering (SSR) and client-side hydration. The framework uses a file-based routing system, where each route corresponds to a .server.jsx or .client.jsx file to determine where the component renders. For instance, a product page at /products/1 maps to routes/products/[handle].server.jsx to ensure SSR for initial load performance.

React Server Components enable partial hydration, reducing client-side JavaScript overhead. A Hydrogen storefront streams HTML from the server, hydrating only interactive elements like cart buttons. The useShopQuery hook fetches data during SSR, binding GraphQL responses to components before the page loads.

Example: A Server Component Fetching Product Data

code
// routes/products/[handle].server.jsx
code
export default function Product({params}) {
code
const {data} = useShopQuery({
code
query: `query Product($handle: String!) {
code
product(handle: $handle) {
code
title
code
description
code
}
code
}`,
code
variables: {handle: params.handle},
code
});
code
return <div>{data.product.title}</div>;
code
}

Explanation:

  • Receives params containing the dynamic handle from the URL.
  • Calls useShopQuery to execute a GraphQL query that fetches product data based on the handle.
  • Sends the handle as a variable to the query using variables: {handle: params.handle}.
  • Queries the product’s title and description fields from the storefront API.
  • Renders the product’s title inside a <div> as part of the component output.

Data Layer & API Integration

Hydrogen relies on Shopify’s Storefront API for all data operations, accessed via useShopQuery for SSR or useQuery for client-side fetching. The framework enforces a GraphQL-first approach, ensuring efficient data retrieval.

Example: A dynamic route fetching product details, including video metadata stored in metafields

code
// Fetching product with video metafield
code
const {data} = useShopQuery({
code
query: `query ProductWithVideo($handle: String!) {
code
product(handle: $handle) {
code
title
code
metafield(namespace: "media", key: "video_url") {
code
value
code
}
code
}
code
}`,
code
variables: {handle: params.handle},
code
});

Explanation:

  • Defines the query ProductWithVideo, which takes a required handle as a variable.
  • Queries the title of the product and a specific metafield with namespace "media" and key "video_url".
  • Accesses the value of the metafield, which contains the video URL.
  • Passes the handle dynamically using variables: {handle: params.handle}.

Getting Started Setup

Initializing a Hydrogen project requires the Shopify CLI:

code
npm init @shopify/hydrogen@latest
code
cd my-hydrogen-store
code
npm run dev

The default template includes a /products route demonstrating SSR and hydration. Running npm run dev starts the Vite server with Hot Module Replacement (HMR) for rapid iteration.

Embedding Videos in Shopify Hydrogen

Handling Video Assets in Hydrogen

Video content in Hydrogen often resides in Shopify metafields, retrievable via GraphQL. A product video can be rendered using the native <video> tag or libraries like react-player for advanced features:

code
// Example: Video component
code
function ProductVideo({url}) {
code
return (
code
<video controls preload="metadata">
code
<source src={url} type="video/mp4" />
code
</video>
code
);
code
}

Explanation:

  • Uses the <video> element with controls to enable playback controls for the user.
  • Sets preload="metadata" to load only video metadata (like duration and dimensions) before playback starts.
  • Includes a <source> element with src={url} and type="video/mp4" to specify the video file and format.

Rendering Video Content Per Product

A dynamic product page with video embeds combines server-side data fetching with lazy loading. Below is a complete example:

code
code
// routes/products/[handle].server.jsx
code
import {Suspense} from 'react';
code
import ProductVideo from '../../components/ProductVideo.client';
code

code
export default function ProductPage({params}) {
code
const {data} = useShopQuery({
code
query: `query ProductVideo($handle: String!) {
code
product(handle: $handle) {
code
title
code
metafield(namespace: "media", key: "trailer_url") {
code
value
code
}
code
}
code
}`,
code
variables: {handle: params.handle},
code
});
code

code
return (
code
<div>
code
<h1>{data.product.title}</h1>
code
<Suspense fallback={<div>Loading video...</div>}>
code
<ProductVideo url={data.product.metafield?.value} />
code
</Suspense>
code
</div>
code
);
code
}

Example:

  • Imports Suspense from React to enable lazy loading of client-side components.
  • Imports ProductVideo from a client-side component located in components/ProductVideo.client.
  • Defines the ProductPage component to receive params containing the dynamic product handle.
  • Executes a GraphQL query using useShopQuery to fetch the product's title and a video metafield with the key "trailer_url" in the "media" namespace.
  • Passes the handle dynamically to the query using variables: {handle: params.handle}.
  • Renders the product title inside an <h1> element.
  • Wraps the ProductVideo component in a <Suspense> block with a fallback message shown while the video component loads.
  • Lazy loading with Suspense prevents blocking the main thread, while autoplay logic can be controlled via React hooks.

Performance Optimization for Video Storefronts

Hydrogen’s streaming SSR reduces Largest Contentful Paint (LCP) by sending HTML chunks early. For video-heavy pages, preloading metadata and using adaptive streaming (e.g., HLS for mobile) ensures smooth playback. Oxygen’s edge caching stores video thumbnails globally, reducing latency:

code
// Middleware for regional video quality
code
export async function middleware(request) {
code
const country = request.headers.get('cf-ipcountry');
code
const isMobile = request.headers.get('sec-ch-ua-mobile') === '?1';
code
const videoQuality = isMobile ? 'mobile_480p' : 'desktop_1080p';
code
return new Response(null, {
code
headers: {'x-video-quality': videoQuality},
code
});
code
}

Explanation:

  • Detects if the request comes from a mobile device by checking if the sec-ch-ua-mobile header equals '?1'.
  • Determines the appropriate video quality: assigns 'mobile_480p' for mobile users and 'desktop_1080p' for others.
  • Returns a new HTTP response with an x-video-quality custom header set to the selected video quality value.