dist/codex/shopify-commerce/skills/shopify-hydrogen/SKILL.md
Build headless Shopify storefronts with Hydrogen — Remix-based framework, Oxygen deployment, storefront.query(), caching strategies, cart, customer accounts, SEO, and analytics. Use when building custom Shopify storefronts.
npx skillsauth add orcaqubits/agentic-commerce-claude-plugins shopify-hydrogenInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Fetch live docs:
https://shopify.dev/docs/storefronts/headless/hydrogen for Hydrogen documentationsite:shopify.dev hydrogen remix loader action for data fetching patternssite:github.com shopify hydrogen for source, examples, and demo storesite:shopify.dev hydrogen cart for current cart handler APIsite:shopify.dev hydrogen customer accounts for authentication flowHydrogen is Shopify's React-based framework for headless commerce:
DEPRECATION: The JS Buy SDK (EOL July 2025) should NOT be used. Use Hydrogen or the Storefront API directly.
npm create @shopify/hydrogen@latest -- --template demo-store
# or
shopify hydrogen init
app/
├── components/ # Shared React components
├── lib/
│ └── context.ts # Storefront client setup
├── routes/
│ ├── _index.tsx # Homepage
│ ├── products.$handle.tsx # Product detail (dynamic route)
│ ├── collections.$handle.tsx
│ ├── cart.tsx # Cart page
│ └── account.tsx # Customer account
├── entry.server.tsx # Server entry point
└── root.tsx # Root layout
The storefront client is created in your app context and used in loaders:
// Pattern: create storefront client in context
// Fetch live docs for current createStorefrontClient options
const { storefront } = createStorefrontClient({
storeDomain: env.PUBLIC_STORE_DOMAIN,
storefrontApiVersion: env.PUBLIC_STOREFRONT_API_VERSION,
publicStorefrontToken: env.PUBLIC_STOREFRONT_API_TOKEN,
});
// Pattern: loader fetches data via storefront.query with caching
// Fetch live docs for current Storefront API fields and query syntax
export async function loader({ params, context }: LoaderFunctionArgs) {
const { storefront } = context;
const { product } = await storefront.query(PRODUCT_QUERY, {
variables: { handle: params.handle },
cache: CacheLong(), // apply caching strategy
});
if (!product) throw new Response("Not found", { status: 404 });
return json({ product });
}
export default function ProductPage() {
const { product } = useLoaderData<typeof loader>();
return <div><h1>{product.title}</h1></div>;
}
Fetch live docs for the current Storefront API product query fields — the schema expands quarterly.
// Pattern: action handles form submissions (e.g., cart mutations)
export async function action({ request, context }: ActionFunctionArgs) {
const { cart } = context;
const formData = await request.formData();
// Fetch live docs for current cart handler methods
const result = await cart.addLines([
{ merchandiseId: formData.get("variantId"), quantity: 1 },
]);
return json(result);
}
| Strategy | Behavior | Use For |
|----------|----------|---------|
| CacheLong() | Long TTL + long SWR | Products, collections, pages |
| CacheShort() | Short TTL + short SWR | Cart, dynamic content |
| CacheNone() | No caching | Customer-specific data |
| CacheCustom({...}) | Custom maxAge + SWR | Fine-tuned scenarios |
Applied as second argument to storefront.query().
Fetch live docs for exact TTL values —
CacheLongandCacheShortdefault durations may change across Hydrogen versions.
// Pattern: defer non-critical data for progressive rendering
export async function loader({ context }: LoaderFunctionArgs) {
// Critical — awaited before render
const collection = await context.storefront.query(COLLECTION_QUERY);
// Non-critical — streamed after initial render
const recommendations = context.storefront.query(RECS_QUERY);
return defer({ collection, recommendations });
}
export default function Page() {
const { collection, recommendations } = useLoaderData<typeof loader>();
return (
<div>
<h1>{collection.title}</h1>
<Suspense fallback={<p>Loading...</p>}>
<Await resolve={recommendations}>
{(data) => <RecommendedProducts data={data} />}
</Await>
</Suspense>
</div>
);
}
// Pattern: route-level error handling
import { isRouteErrorResponse, useRouteError } from "@remix-run/react";
export function ErrorBoundary() {
const error = useRouteError();
if (isRouteErrorResponse(error)) {
return <div><h1>{error.status === 404 ? "Not Found" : "Error"}</h1></div>;
}
return <div><h1>Something went wrong</h1></div>;
}
Built-in cart management:
createCartHandler() — server-side cart APIuseFetcher for cart mutations (avoids full page navigation)Fetch live docs: Web-search
site:shopify.dev hydrogen createCartHandlerfor current cart handler methods and return types.
New Customer Account API (OAuth-based):
/account/login → redirects to Shopify-hosted login (passwordless)/account/authorize → callback with tokensFetch live docs: Web-search
site:shopify.dev hydrogen customer accountsfor current OAuth flow, Customer Account API queries, and route setup.
Built-in SEO utilities:
getSeoMeta() — generates meta tags from Storefront API dataFetch live docs: Web-search
site:shopify.dev hydrogen seofor currentgetSeoMetaAPI and structured data helpers.
Shopify's edge hosting:
Alternative hosts: Vercel, Cloudflare Workers, Node.js servers (any platform that runs Remix).
CacheLong for stable data, CacheShort for dynamic)defer() for non-critical data to avoid blocking render<Await> component for deferred data rendering?width=, ?crop=)useFetcher for cart mutations (avoids full page navigation)<Link prefetch="intent">Fetch the Hydrogen docs, Remix documentation, and Hydrogen GitHub source for exact loader/action patterns, caching APIs, cart handler methods, and deployment configuration before implementing.
development
Build with Spree's headless Next.js storefront — the official `spree/storefront` repo (Next.js 16 App Router with Server Actions and Turbopack, React 19 Server Components, Tailwind CSS 4, TypeScript 5, `@spree/sdk`, Sentry), server-only auth (httpOnly JWT cookies + publishable key), MeiliSearch faceted catalog, one-page checkout with Apple/Google Pay/Klarna/Affirm/SEPA, multi-region market routing, GA4 + JSON-LD SEO, and Vercel/Docker deployment. Use when forking or customizing the storefront, or evaluating headless adoption.
tools
Build Spree extensions as Rails engines — gem scaffolding, `bin/rails g spree:extension`, mounting routes/migrations/assets, the modern `prepend` decorator pattern (`*_decorator.rb` with `self.prepended(base)`), generators (`spree:model_decorator`, `spree:controller_decorator`), the four customization surfaces in preference order (Events > Webhooks > Dependencies > Decorators), Spree::Dependencies for swapping service objects, gem release/versioning, and the deprecated Deface engine. Use when building a reusable Spree extension or adding non-trivial customization to an app.
development
Build with Spree's event bus and Webhooks 2.0 — `Spree::Events` publication, `Spree::Subscriber` DSL with `subscribes_to` and `on`, wildcard matching, lifecycle events (`{model}.created/.updated/.deleted` via `publishes_lifecycle_events`), the canonical event catalog (order.*, payment.*, shipment.*, product.*), Webhooks 2.0 endpoints, HMAC-SHA256 signing (`X-Spree-Webhook-Signature`), exponential-backoff retries, and Sidekiq job orchestration. Use when wiring event-driven business logic, building webhook consumers, or replacing ActiveSupport callback chains.
tools
Cross-cutting Spree development patterns — the customization preference hierarchy (Events > Webhooks > Dependencies > Decorators), `Spree::Dependencies` service-object swapping, the `_decorator.rb` + `prepend` + `self.prepended` idiom, idempotent subscribers and webhook receivers, multi-store scoping discipline, prefixed IDs, calculator polymorphism (shipping/promotion/tax share the base), service-object composition with `dry-monads` or simple results, why to avoid `class_eval` reopening and Deface, and Spree-on-Rails idioms (Hotwire/Turbo Stimulus, ActiveStorage, Action Cable, Sidekiq). Use when designing the architecture of a Spree extension or solving cross-cutting concerns.