shopify-hydrogen

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

11 stars

Best use case

shopify-hydrogen is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

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

Teams using shopify-hydrogen should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/shopify-hydrogen/SKILL.md --create-dirs "https://raw.githubusercontent.com/finsilabs/awesome-ecommerce-skills/main/skills/headless-modern/shopify-hydrogen/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/shopify-hydrogen/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How shopify-hydrogen Compares

Feature / Agentshopify-hydrogenStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

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

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# 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',
      reverse: sortKey === 'PRICE' ? false : true,
    },
    cache: storefront.CacheShort(),
  });

  return json({collection});
}

const COLLECTION_QUERY = `#graphql
  query Collection(
    $handle: String!
    $first: Int
    $sortKey: ProductCollectionSortKeys
    $reverse: Boolean
  ) {
    collection(handle: $handle) {
      id
      title
      description
      image { url altText }
      products(first: $first, sortKey: $sortKey, reverse: $reverse) {
        nodes {
          id
          title
          handle
          priceRange { minVariantPrice { amount currencyCode } }
          featuredImage { url altText }
        }
        pageInfo { hasNextPage endCursor }
      }
    }
  }
` as const;
```

### Customer authentication with new Customer Account API

```typescript
// Hydrogen supports the new Customer Account API (OAuth-based)
// app/lib/customer-account.server.ts
export async function loader({context}: LoaderFunctionArgs) {
  const {customerAccount} = context;
  const isLoggedIn = await customerAccount.isLoggedIn();

  if (!isLoggedIn) {
    return redirect('/account/login');
  }

  const {data} = await customerAccount.query(`#graphql
    query Customer {
      customer {
        id
        firstName
        lastName
        emailAddress { emailAddress }
        orders(first: 10) {
          nodes {
            id
            number
            processedAt
            financialStatus
            totalPrice { amount currencyCode }
          }
        }
      }
    }
  `);

  return json({customer: data.customer});
}
```

## Best Practices

- **Use `storefront.CacheLong()` for catalog data** — product and collection data rarely changes; long cache TTLs dramatically improve TTFB on Oxygen's edge network
- **Colocate GraphQL queries with routes** — define `as const` fragment strings in the same file as the loader; this keeps data requirements visible and enables TypeScript inference
- **Use Hydrogen's `<Image>` and `<Money>` components** — they handle Shopify CDN image optimization URLs and currency formatting automatically
- **Leverage Remix defer + Suspense for non-critical data** — render the product immediately and stream recommendations or reviews with `defer()`
- **Keep cart state server-side via cookies** — Hydrogen's cart utilities store the cart ID in a signed cookie; avoid client-only cart state that breaks SSR
- **Use the Storefront API's `@inContext` directive** — pass `language` and `country` context to get localized prices and translated content per request
- **Pin the Storefront API version in `.env`** — Shopify deprecates old API versions; explicit pinning prevents surprise breakage on API updates

## Common Pitfalls

| Problem | Solution |
|---------|----------|
| "Storefront API token not authorized" | Ensure the token has `unauthenticated_read_*` scopes; private tokens are only for server-side requests |
| Cart state lost between page navigations | Store cart ID in the session cookie using `cart.setCartId()`; never store cart ID in component state |
| Images not optimized on Oxygen | Use Hydrogen's `<Image>` component or the `getImageData()` helper to append Shopify CDN transform params |
| TypeScript errors on GraphQL queries | Run `npm run codegen` to regenerate types after changing queries; queries must be tagged `as const` |
| Deployment fails with "missing environment variables" | Oxygen env vars must be added in the Shopify admin under the Hydrogen deployment settings, not just in `.env` |

## Related Skills

- @saleor-development
- @jamstack-storefront
- @composable-commerce
- @pwa-storefront
- @commerce-api-gateway

Related Skills

shopify-webhooks

11
from finsilabs/awesome-ecommerce-skills

Register, verify, and reliably process Shopify webhook events for orders, inventory, and customers with HMAC validation and idempotency handling

shopify-theme-development

11
from finsilabs/awesome-ecommerce-skills

Build and customize Shopify themes using Liquid templating, JSON sections, dynamic blocks, and theme app extensions for added functionality

shopify-storefront-api

11
from finsilabs/awesome-ecommerce-skills

Build a headless Shopify frontend using the GraphQL Storefront API for product queries, cart management, and checkout with the Buy SDK

shopify-metafields

11
from finsilabs/awesome-ecommerce-skills

Store custom data on any Shopify resource — products, orders, customers — using typed metafield definitions accessible from Liquid and the Storefront API

shopify-checkout-extensions

11
from finsilabs/awesome-ecommerce-skills

Customize Shopify's checkout with UI extensions for upsells and custom fields, plus Shopify Functions for serverless discount and shipping logic

wishlist-save-for-later

11
from finsilabs/awesome-ecommerce-skills

Let shoppers save products to a wishlist, share it with friends, and get notified when saved items come back in stock or drop in price

storefront-theming

11
from finsilabs/awesome-ecommerce-skills

Build a themeable storefront with design tokens and CSS custom properties that supports white-labeling, multi-brand variants, and dark mode

search-autocomplete

11
from finsilabs/awesome-ecommerce-skills

Speed up product discovery with instant search suggestions, fuzzy typo matching, and category-aware results powered by Algolia or Elasticsearch

responsive-storefront

11
from finsilabs/awesome-ecommerce-skills

Build a mobile-first storefront with thumb-friendly navigation, sticky add-to-cart buttons, and touch-optimized components for high mobile conversion

recently-viewed-products

11
from finsilabs/awesome-ecommerce-skills

Show shoppers the products they recently browsed using browser storage so they can easily pick up where they left off on your store

quick-view-modal

11
from finsilabs/awesome-ecommerce-skills

Let shoppers preview product details and add items to cart from the listing page without navigating away, reducing friction in the shopping flow

product-page-design

11
from finsilabs/awesome-ecommerce-skills

Design high-converting product detail pages with image galleries, variant selectors, social proof, and clear calls-to-action that drive add-to-cart