Claude
Skills
Sign in
Back

shopify-hydrogen

Included with Lifetime
$97 forever

Build a custom Shopify storefront using the Hydrogen React framework with Remix routing and deploy it to Shopify's Oxygen edge hosting

headless-modernshopifyhydrogenremixoxygenstorefront-apiheadlessreactvite

What this skill does


# Shopify Hydrogen

## Overview

Hydrogen is Shopify's official React-based framework for building headless storefronts, built on top of Remix and deployed to Oxygen (Shopify's edge hosting). It provides first-class primitives for the Storefront API — product queries, cart management, customer accounts — alongside Shopify-specific components and hooks that handle caching, streaming, and SEO automatically. This skill covers scaffolding a Hydrogen project, querying the Storefront API, implementing cart functionality, and deploying to Oxygen.

## When to Use This Skill

- When building a custom Shopify storefront with full design and UX control
- When the default Shopify Online Store theme is too limiting for your design requirements
- When you need server-side rendering, streaming, and edge-deployed performance
- When integrating third-party services (loyalty, CMS, personalization) directly into the storefront
- When you want a Shopify-managed backend with a completely custom frontend stack

## Prerequisites & Platform Notes

**This skill is written for custom/headless storefronts** (Node.js, Python, or similar backend). The code examples use TypeScript/Node.js and can be adapted to any stack.

**Shopify**: Shopify Hydrogen is Shopify's headless framework. MACH/composable patterns apply when using Shopify as the commerce backend with a custom frontend, or when mixing Shopify with other best-of-breed services.
**WooCommerce**: WooCommerce can serve as a headless backend via its REST API and WPGraphQL. These patterns apply when decoupling the frontend from WordPress.
**Magento**: Magento's GraphQL API and PWA Studio support headless architectures. These composable patterns apply to Magento as a backend service in a MACH stack.

**You'll need**:
- Node.js 18+ (or adapt to your backend language)
- Redis for caching/queues
- An email sending service (SendGrid, AWS SES, or Postmark)
- CDN (Cloudflare, CloudFront, or Fastly)

## Core Instructions

1. **Scaffold a Hydrogen project**

   ```bash
   npm create @shopify/hydrogen@latest -- --quickstart
   # or with options:
   npm create @shopify/hydrogen@latest
   # Follow prompts: project name, language (TypeScript), mock shop or real credentials
   cd my-hydrogen-store
   npm run dev
   # http://localhost:3000
   ```

   The project structure follows Remix file-based routing:
   ```
   app/
     routes/
       _index.tsx         # Homepage
       products.$handle.tsx  # Product detail page
       collections.$handle.tsx
       cart.tsx
     components/
     lib/
       fragments.ts       # Reusable GraphQL fragments
   server.ts              # Hydrogen + Remix entry point
   ```

2. **Configure Storefront API credentials**

   Create a Storefront API token in your Shopify admin under **Apps → Develop apps → Create an app → Storefront API**.

   ```bash
   # .env
   SESSION_SECRET="your-session-secret"
   PUBLIC_STOREFRONT_API_TOKEN="your-public-token"
   PUBLIC_STORE_DOMAIN="your-store.myshopify.com"
   PUBLIC_STOREFRONT_API_VERSION="2025-01"
   ```

   The `server.ts` wires Hydrogen into Remix:
   ```typescript
   import {createHydrogenContext} from '@shopify/hydrogen';

   const hydrogenContext = createHydrogenContext({
     storefront: {
       apiVersion: env.PUBLIC_STOREFRONT_API_VERSION,
       privateStorefrontToken: env.PRIVATE_STOREFRONT_API_TOKEN,
       publicStorefrontToken: env.PUBLIC_STOREFRONT_API_TOKEN,
       storeDomain: env.PUBLIC_STORE_DOMAIN,
     },
     session: HydrogenSession.init(request, [env.SESSION_SECRET]),
   });
   ```

3. **Query the Storefront API**

   Hydrogen provides a `storefront.query` method with built-in caching policies.

   ```typescript
   // app/routes/products.$handle.tsx
   import {useLoaderData} from '@remix-run/react';
   import {json, type LoaderFunctionArgs} from '@shopify/remix-oxygen';

   export async function loader({params, context}: LoaderFunctionArgs) {
     const {storefront} = context;
     const {product} = await storefront.query(PRODUCT_QUERY, {
       variables: {handle: params.handle},
       cache: storefront.CacheLong(), // Cache at CDN for 24h
     });

     if (!product) throw new Response('Not Found', {status: 404});
     return json({product});
   }

   const PRODUCT_QUERY = `#graphql
     query Product($handle: String!) {
       product(handle: $handle) {
         id
         title
         descriptionHtml
         featuredImage { url altText width height }
         variants(first: 20) {
           nodes {
             id
             title
             price { amount currencyCode }
             availableForSale
             selectedOptions { name value }
           }
         }
       }
     }
   ` as const;
   ```

4. **Implement cart with Hydrogen cart utilities**

   Hydrogen provides server-side cart actions via Remix action functions:

   ```typescript
   // app/routes/cart.tsx
   import {CartForm} from '@shopify/hydrogen';
   import type {ActionFunctionArgs} from '@shopify/remix-oxygen';

   export async function action({request, context}: ActionFunctionArgs) {
     const {cart} = context;
     const formData = await request.formData();
     const {action, inputs} = CartForm.getFormInput(formData);

     let result;
     switch (action) {
       case CartForm.ACTIONS.LinesAdd:
         result = await cart.addLines(inputs.lines);
         break;
       case CartForm.ACTIONS.LinesUpdate:
         result = await cart.updateLines(inputs.lines);
         break;
       case CartForm.ACTIONS.LinesRemove:
         result = await cart.removeLines(inputs.lineIds);
         break;
       default:
         throw new Error(`Unhandled cart action: ${action}`);
     }

     const headers = cart.setCartId(result.cart.id);
     return json(result, {headers});
   }

   // Add to cart form component
   export function AddToCartButton({variantId}: {variantId: string}) {
     return (
       <CartForm
         route="/cart"
         action={CartForm.ACTIONS.LinesAdd}
         inputs={{lines: [{merchandiseId: variantId, quantity: 1}]}}
       >
         <button type="submit">Add to Cart</button>
       </CartForm>
     );
   }
   ```

5. **Use Hydrogen caching strategies**

   Hydrogen exposes named caching strategies that map to CDN cache-control headers:

   ```typescript
   // Long cache for static catalog data
   const {collections} = await storefront.query(COLLECTIONS_QUERY, {
     cache: storefront.CacheLong(), // s-maxage=3600, stale-while-revalidate=82800
   });

   // Short cache for inventory-sensitive data
   const {product} = await storefront.query(PRODUCT_WITH_INVENTORY, {
     cache: storefront.CacheShort(), // s-maxage=1, stale-while-revalidate=9
   });

   // No cache for personalized/cart data
   const {customer} = await storefront.query(CUSTOMER_QUERY, {
     cache: storefront.CacheNone(),
   });

   // Custom strategy
   const {data} = await storefront.query(QUERY, {
     cache: storefront.CacheCustom({
       mode: 'public',
       maxAge: 600,
       staleWhileRevalidate: 3000,
     }),
   });
   ```

6. **Deploy to Oxygen**

   ```bash
   npm install -g @shopify/cli
   shopify hydrogen deploy
   # Creates a deployment in your Shopify admin under Online Store → Themes → Headless
   ```

   For CI/CD, use the GitHub Action:
   ```yaml
   # .github/workflows/oxygen.yml
   - uses: Shopify/hydrogen-action@v1
     with:
       shop: ${{ secrets.SHOPIFY_SHOP_DOMAIN }}
       token: ${{ secrets.SHOPIFY_CLI_TOKEN }}
   ```

## Examples

### Collection page with filtering and sorting

```typescript
// app/routes/collections.$handle.tsx
export async function loader({params, request, context}: LoaderFunctionArgs) {
  const {storefront} = context;
  const url = new URL(request.url);
  const sortKey = url.searchParams.get('sort') as ProductCollectionSortKeys | null;

  const {collection} = await storefront.query(COLLECTION_QUERY, {
    variables: {
      handle: params.handle,
      first: 24,
      sortKey: sortKey ?? 'BEST_SELLING',
      rev

Related in headless-modern