DDS Vibe Academy Hydrogen 2026.4.0 Storefront API 2026-04
Masterclass 26 / Apr 2026

Build Headless Shopify Storefronts
with Hydrogen 2026.

From Liquid to React Router v7, Oxygen, and Storefront MCP. The honest playbook for vibe-coding a production headless storefront, including how to wire the DDS Sovereign AGI Suite directly into the storefront via Model Context Protocol.

8
Modules
50
AI Prompts
~8h
Workload
$0
Course Cost
Quick Answer

Shopify Hydrogen 2026.4 is a React Router v7 framework for headless storefronts, hosted free on Cloudflare-powered Oxygen and integrated with Shopify's Storefront API and the new Storefront MCP for AI agents.

It is the right choice when your team has React expertise, your UX exceeds what Liquid can express, and you have engineering budget for ongoing optimization. It is the wrong choice when a tuned Liquid theme would already get you 90 percent of the way there.

This masterclass walks through eight modules from environment setup to wiring synthetic AI employees into the storefront, with full code examples calibrated for Vibe Coding workflows.

Author Robert McCullock, Architect-CEO, Design Delight Studio
Reality Check DDS itself runs on Atelier 3.4.0 Liquid. Module 8 explains why.
Verified Against Hydrogen 2026.4.0 release notes, Apr 2026
Who This Is For

Three personas, three reasons to read this.

Hydrogen is not a universal upgrade from Liquid. The economics differ wildly by team shape and brand maturity. Pick the persona closest to your situation.

Solo Founder

You vibe-code your storefront alone.

You ship features yourself with Claude Code or Antigravity. You evaluate Hydrogen for control and AI integration upside.

  • You will benefit most from Modules 1, 6, and 8
  • You need Module 1's honest cost framing first
  • Storefront MCP is the feature most likely to justify migration
Agency Developer

You ship Hydrogen for clients.

You need a current reference for React Router v7 patterns, the 2026.4 breaking changes, and a defensible decision matrix for client conversations.

  • Modules 3, 4, 5, and 7 are your daily reference
  • Module 6 sells AI integration as a billable phase
  • Module 1 protects you from bad headless engagements
Brand CTO

You decide whether to migrate.

You need the cost-benefit math, the breaking-change inventory, and a reality check on app compatibility before greenlighting a Hydrogen migration.

  • Modules 1 and 8 give you the decision framework
  • Modules 5 and 7 expose the operational surface area
  • Module 6 quantifies the AI-integration upside
MODULE 01 / 08

Foundations and the Honest Decision

Read this module before doing anything else. It is the single most cost-saving section in the masterclass.

You will leave this module able to:

  • Articulate what headless commerce actually is at the network layer
  • Apply an eight-axis decision matrix to your specific store
  • Quantify the real three-year cost of going headless

What headless actually means

A standard Shopify storefront renders pages on Shopify's servers from .liquid templates and ships HTML to the browser. The customer's browser loads HTML, then progressively enhances with JavaScript. One server, one runtime, one domain.

A headless storefront splits that. Shopify exposes commerce data through the GraphQL Storefront API. Your storefront is a separate React application, hosted somewhere else, that fetches that data and renders pages. The "head" (the frontend) is decoupled from the "body" (Shopify's commerce engine).

Hydrogen 2026.4 is Shopify's opinionated React framework for that head. It runs on React Router v7 with Vite as the build tool, deploys to Oxygen at the edge, and ships with Shopify-aware primitives like cart helpers, customer account flows, and now Storefront MCP for AI agent integration.

The eight-axis decision matrix

Score your project from 1 (favors Liquid) to 5 (favors Hydrogen) on each axis. Sum and read the verdict.

Axis Liquid (1) Hydrogen (5)
Team React skillNone or juniorSenior React + TypeScript fluency
App ecosystem dependencyHeavy reliance on Liquid-only appsAPI-first or custom integration acceptable
Performance ceiling needsPageSpeed 75-85 acceptableSub-second LCP non-negotiable
UX customization needsStandard PDP, PLP, cart sufficeHighly custom interactions, animations, app-like feel
Engineering budgetLimited or noneOne to three FTE engineers committed
Time-to-launch pressureNeed to ship in weeksCan invest months in build
Content workflow toleranceMarketers must edit visuallyContent team accepts CMS or PR-based edits
AI-agent strategyNot a priorityStorefront MCP unlocks core business value
  • Score 8-16: Stay on Liquid. The migration will not pay back.
  • Score 17-28: Mixed signal. Pilot Hydrogen on a subset of pages or campaigns.
  • Score 29-40: Hydrogen is the right architecture. Begin Module 2.
Cost Reality

Industry build estimates for a production Hydrogen storefront span approximately 150,000 to 700,000 EUR with 1 to 3 FTE engineers, plus third-party services like a CMS, monitoring, and search. Oxygen hosting itself is free. The cost is people, content tooling, and ongoing optimization. This number is from public Plus Partner assessments published in early 2026 and is approximate.

Why DDS still runs on Liquid

Design Delight Studio is a solo-founder operation on Shopify Basic with one storefront, ~6 SKUs, and a heavy dependency on Liquid-compatible apps. Score on the matrix: roughly 14 of 40. Hydrogen would not pay back. This is the same calculus you should run for your own brand.

MODULE 02 / 08

Environment Setup, Vibe Coder Edition

This module gets you from a clean machine to a running Hydrogen 2026.4 dev server with Oxygen and Storefront API connected. Estimated time: 45 to 90 minutes.

You will leave this module with:

  • A scaffolded Hydrogen 2026.4 project running locally on Miniflare v3
  • Storefront API and Customer Account API tokens wired and tested
  • Claude Code or Antigravity attached to the repository for vibe coding

Prerequisites

  • Node.js 20 or later (Hydrogen 2026.4 requires it)
  • npm 10 or pnpm 9
  • A Shopify development store or paid plan
  • A GitHub account for Oxygen deployment
  • Claude Code, Antigravity, or your AI coding tool of choice

Step 1: scaffold the project

The current starter scaffolds a complete React Router v7 project pinned to Hydrogen 2026.4 with Storefront API 2026-04, Vite, Miniflare v3, and codegen wired in.

# Create a new Hydrogen project npm create @shopify/hydrogen@latest # Choose the JavaScript or TypeScript starter # (recommend TypeScript for type safety on loaders) cd your-project-name npm install npm run dev

The dev server starts on Miniflare v3 (the local emulator for Cloudflare workerd, which Oxygen runs in production). You will see the starter on http://localhost:3000.

Step 2: package versions you should see

Verify your package.json has the expected versions. Mismatched versions are the most common Hydrogen onboarding bug.

{ "scripts": { "build": "shopify hydrogen build --codegen", "dev": "shopify hydrogen dev --codegen", "preview": "shopify hydrogen preview --build", "typecheck": "react-router typegen && tsc --noEmit", "codegen": "shopify hydrogen codegen && react-router typegen" }, "dependencies": { "@shopify/hydrogen": "2026.4.0", "react": "18.3.1", "react-dom": "18.3.1", "react-router": "^7.12.0", "react-router-dom": "^7.12.0" }, "devDependencies": { "@shopify/cli": "3.85.4", "@shopify/hydrogen-codegen": "^0.3.3", "@shopify/mini-oxygen": "^4.0.0", "@react-router/dev": "^7.12.0", "@react-router/fs-routes": "^7.12.0" } }
Migration Note

If you are upgrading an existing project, run npx shopify hydrogen upgrade. The CLI ships codemods that rewrite imports from @shopify/remix-oxygen to @shopify/hydrogen/oxygen and update the React Router peer dependency ranges. The 2026.4 release also removed the proxyStandardRoutes option from createRequestHandler; the codemod will strip it.

Step 3: connect Storefront API

The starter includes a sample .env file. Copy it to .env.local and populate the four required values from your Shopify admin.

# .env.local SESSION_SECRET="generate-a-32-char-random-string" PUBLIC_STOREFRONT_API_TOKEN="shpat_..." PUBLIC_STOREFRONT_ID="your-shop-handle" PUBLIC_STORE_DOMAIN="your-shop.myshopify.com" # Customer Account API (optional, required for login routes) PUBLIC_CUSTOMER_ACCOUNT_API_CLIENT_ID="..." PUBLIC_CUSTOMER_ACCOUNT_API_URL="https://shopify.com/your-shop-id/account" # Storefront MCP (added in Winter '26) PUBLIC_CHECKOUT_DOMAIN="your-shop.myshopify.com"

Find these values in Settings > Apps > Develop apps. Create a custom Storefront API access token with the standard read scopes plus unauthenticated_read_customers if you need customer account flows.

Step 4: wire your AI coding agent

Vibe Coding on Hydrogen requires the agent to understand the file conventions. Add this CLAUDE.md at the repo root for Claude Code, or an equivalent .antigravity/agent.md for Antigravity.

# Project Conventions for AI Agents This is a Shopify Hydrogen 2026.4 storefront on React Router v7. ## File conventions - Routes live in `app/routes/` using flat-file naming - GraphQL fragments in `app/lib/fragments.ts` - Reusable components in `app/components/` - All loaders and actions return JSON via `{ data, headers }` pattern - All mutations go through `action` exports, never directly from components ## Caching - Use `CacheLong` for product detail pages and collections - Use `CacheShort` for cart and customer-specific data - Use `CacheNone` for any authenticated route ## Type safety - React Router auto-generates route types in `.react-router/types/` - Import `Route` types: `import type { Route } from "./+types/$handle"` - Never use `any` in loaders or actions ## Forbidden patterns - No client-side fetch to Storefront API (use loaders) - No `useEffect` data fetching (use loaders or middleware) - No global state libraries (Redux, Zustand) without explicit need

This single file dramatically improves the quality of AI-generated code on a Hydrogen project. Without it, agents tend to fall back to outdated Remix or generic React patterns.

MODULE 03 / 08

Architecture Deep Dive

Understanding the layers is what separates a Hydrogen developer from someone copy-pasting from the cookbook. This module covers the runtime, the request lifecycle, and the four primitives you will use every day.

You will leave this module able to:

  • Trace a request from Oxygen edge through React Router v7 to the Storefront API and back
  • Use loaders, actions, middleware, and streaming SSR correctly
  • Apply granular caching with CacheNone, CacheShort, CacheLong, and CacheCustom

The file tree

your-hydrogen-project/ ├── app/ │ ├── components/ # Shared React components │ ├── lib/ │ │ ├── context.ts # createHydrogenContext │ │ ├── fragments.ts # GraphQL fragments (reusable) │ │ └── session.ts # Session storage adapter │ ├── routes/ # File-based routing │ │ ├── _index.tsx # Homepage │ │ ├── products.$handle.tsx │ │ ├── collections.$handle.tsx │ │ ├── cart.tsx │ │ └── api.mcp.tsx # Storefront MCP proxy (auto-handled in 2026.4) │ ├── styles/ │ ├── entry.client.tsx │ ├── entry.server.tsx │ └── root.tsx # Global layout, <html>, <head>, <body> ├── server.ts # Oxygen request handler ├── react-router.config.ts # Router preset wiring Hydrogen ├── vite.config.ts ├── .env.local └── package.json

The request lifecycle

  1. Browser request hits an Oxygen edge node (Cloudflare workerd globally)
  2. server.ts's createRequestHandler receives the request and creates a Hydrogen context via createHydrogenContext
  3. React Router matches the URL to a route module in app/routes/
  4. Any registered middleware runs (auth, logging, redirects)
  5. The route's loader executes server-side and queries the Storefront API
  6. The component renders to streaming HTML; defer wrappers stream in async data as it resolves
  7. HTML streams to the browser; React hydrates progressively

createHydrogenContext (the 2025.7+ way)

This helper assembles the Storefront client, Customer Account client, cart helpers, session, and now the MCP proxy into one context object that is passed to every loader and action.

// app/lib/context.ts import { createHydrogenContext } from "@shopify/hydrogen"; import { AppSession } from "./session"; export async function createAppLoadContext( request: Request, env: Env, executionContext: ExecutionContext ) { const session = await AppSession.init(request, [env.SESSION_SECRET]); const hydrogenContext = createHydrogenContext({ env, request, cache: await caches.open("hydrogen"), waitUntil: executionContext.waitUntil.bind(executionContext), session, i18n: { language: "EN", country: "US" }, cart: { queryFragment: CART_QUERY_FRAGMENT, }, }); return { ...hydrogenContext, // Add your own context extensions here }; }

Route Module Type Safety (new in 2025.7)

React Router v7 auto-generates route-specific types. URL params, loader return types, and action data are all inferred. No more manual type wrangling.

// app/routes/products.$handle.tsx import type { Route } from "./+types/products.$handle"; export async function loader({ params, context }: Route.LoaderArgs) { // params.handle is typed as string automatically const { product } = await context.storefront.query(PRODUCT_QUERY, { variables: { handle: params.handle }, cache: context.storefront.CacheLong(), }); if (!product) throw new Response("Not Found", { status: 404 }); return { product }; } export default function Product({ loaderData }: Route.ComponentProps) { // loaderData.product is fully typed return <h1>{loaderData.product.title}</h1>; }

Middleware (new in 2025.7)

Middleware runs before and after the route's response. Use it for cross-cutting concerns: auth, logging, geo-redirects, header injection.

// app/routes/account.tsx import { redirect } from "react-router"; import type { Route } from "./+types/account"; export const middleware = [ async ({ context, request }: Route.MiddlewareArgs, next) => { const isLoggedIn = await context.customerAccount.isLoggedIn(); if (!isLoggedIn) { return redirect("/account/login"); } const response = await next(); response.headers.set("Cache-Control", "private, no-store"); return response; }, ];

Granular caching

Hydrogen exposes four cache strategies on the Storefront client. Pick the right one for the data's volatility, not for convenience.

StrategyUse forTTL behavior
CacheNone()Cart, customer data, anything authenticatedNever cached
CacheShort()Search results, "recently viewed"~1 second fresh, 9s stale
CacheLong()Product detail, collection lists~1 hour fresh, 23h stale
CacheCustom()When the defaults do not fitYou define both windows
Failure mode

The most common Hydrogen production bug is using CacheLong on a query that includes user-specific data (e.g., a customer-specific price). The first user's price gets served to everyone for an hour. Rule of thumb: if the query includes a buyer identity, cart token, or auth header, default to CacheNone.

Streaming SSR with defer

Slow data (recommendations, third-party APIs) should not block the initial HTML. Wrap the slow promise in defer and render with <Await>.

// In your loader export async function loader({ context }: Route.LoaderArgs) { const product = await context.storefront.query(PRODUCT_QUERY); // critical, awaited const recommendations = context.storefront.query(RECS_QUERY); // slow, deferred (no await) return { product, recommendations }; } // In your component import { Await, useLoaderData } from "react-router"; import { Suspense } from "react"; export default function Product() { const { product, recommendations } = useLoaderData<typeof loader>(); return ( <> <h1>{product.title}</h1> <Suspense fallback={<p>Loading recommendations...</p>}> <Await resolve={recommendations}> {(recs) => <Recommendations data={recs} />} </Await> </Suspense> </> ); }
MODULE 04 / 08

Building the Storefront

This module is the longest in the masterclass. Work through it route by route. Each section ships paste-ready code that compiles against Hydrogen 2026.4.

You will leave this module having shipped:

  • Home, product detail, collection, and cart routes
  • A customer login flow on the Customer Account API
  • Optimistic cart line updates and a working checkout redirect

4.1 Reusable GraphQL fragments

Fragments are how you keep queries DRY. Define them once in app/lib/fragments.ts and import everywhere.

# app/lib/fragments.ts export const PRODUCT_FRAGMENT = `#graphql fragment Product on Product { id handle title description featuredImage { url altText width height } priceRange { minVariantPrice { amount currencyCode } maxVariantPrice { amount currencyCode } } variants(first: 10) { nodes { id title availableForSale selectedOptions { name value } price { amount currencyCode } } } } `; export const COLLECTION_FRAGMENT = `#graphql fragment Collection on Collection { id handle title description image { url altText } products(first: 12) { nodes { ...Product } } } ${'$'}{PRODUCT_FRAGMENT} `;

4.2 Product detail route

// app/routes/products.$handle.tsx import { useLoaderData } from "react-router"; import { Money, Image } from "@shopify/hydrogen"; import type { Route } from "./+types/products.$handle"; import { PRODUCT_FRAGMENT } from "~/lib/fragments"; const PRODUCT_QUERY = `#graphql query Product(${'$'}handle: String!) { product(handle: ${'$'}handle) { ...Product } } ${'$'}{PRODUCT_FRAGMENT} `; export async function loader({ params, context }: Route.LoaderArgs) { const { product } = await context.storefront.query(PRODUCT_QUERY, { variables: { handle: params.handle }, cache: context.storefront.CacheLong(), }); if (!product) throw new Response("Not Found", { status: 404 }); return { product }; } export const meta: Route.MetaFunction = ({ data }) => [ { title: data?.product.title }, { name: "description", content: data?.product.description?.slice(0, 155) }, ]; export default function Product({ loaderData }: Route.ComponentProps) { const { product } = loaderData; return ( <article> {product.featuredImage && ( <Image data={product.featuredImage} aspectRatio="1/1" sizes="(min-width: 768px) 50vw, 100vw" /> )} <h1>{product.title}</h1> <Money data={product.priceRange.minVariantPrice} /> <p>{product.description}</p> <AddToCartForm variants={product.variants.nodes} /> </article> ); }

4.3 Cart with optimistic updates

The Hydrogen cart helper handles the heavy lifting. The pattern below uses an action to mutate, then revalidates loaders automatically.

// app/routes/cart.tsx import { CartForm, type CartActionInput } from "@shopify/hydrogen"; import type { Route } from "./+types/cart"; export async function action({ request, context }: Route.ActionArgs) { 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(`Unsupported cart action: ${'$'}{action}`); } // Handle the new MERCHANDISE_LINE_TRANSFORMERS_RUN_ERROR (2026-04+) const transformerError = result.errors?.find( (e) => e.code === "MERCHANDISE_LINE_TRANSFORMERS_RUN_ERROR" ); if (transformerError) { return { error: "A cart transformer function failed. Please try again." }; } return { cart: result.cart }; } export async function loader({ context }: Route.LoaderArgs) { return { cart: await context.cart.get() }; }
2026-04 Breaking Change

Cart operations now return a specific MERCHANDISE_LINE_TRANSFORMERS_RUN_ERROR error code when a Cart Transform Function fails at runtime, instead of the previous generic INVALID code. If your storefront uses Cart Transform Functions, add explicit handling for this code as shown above.

4.4 Customer Account API login

// app/routes/account.login.tsx import type { Route } from "./+types/account.login"; export async function loader({ context }: Route.LoaderArgs) { return context.customerAccount.login(); } // app/routes/account.authorize.tsx export async function loader({ context }: Route.LoaderArgs) { return context.customerAccount.authorize(); } // app/routes/account.logout.tsx export async function action({ context }: Route.ActionArgs) { return context.customerAccount.logout(); }

The Customer Account API uses OAuth via Shopify's hosted login. You do not store passwords. The three routes above are the entire auth surface.

4.5 Collection page with pagination

Use the new infinite-scroll recipe from the Hydrogen Cookbook for collection pagination. The pattern below uses cursor-based pagination via the Pagination helper.

// app/routes/collections.$handle.tsx import { Pagination, getPaginationVariables } from "@shopify/hydrogen"; import type { Route } from "./+types/collections.$handle"; export async function loader({ params, context, request }: Route.LoaderArgs) { const paginationVariables = getPaginationVariables(request, { pageBy: 12 }); const { collection } = await context.storefront.query(COLLECTION_QUERY, { variables: { handle: params.handle, ...paginationVariables }, cache: context.storefront.CacheLong(), }); if (!collection) throw new Response("Not Found", { status: 404 }); return { collection }; } export default function Collection({ loaderData }: Route.ComponentProps) { return ( <Pagination connection={loaderData.collection.products}> {({ nodes, NextLink, PreviousLink, isLoading }) => ( <> <PreviousLink>{isLoading ? "Loading..." : "Previous"}</PreviousLink> <ProductGrid products={nodes} /> <NextLink>{isLoading ? "Loading..." : "Next"}</NextLink> </> )} </Pagination> ); }
MODULE 05 / 08

Performance and SEO

Hydrogen is faster than Liquid only when you tune it correctly. This module covers the levers that move Core Web Vitals and the structured data layer that wins rich results.

You will leave this module able to:

  • Hit sub-1.5s LCP on product pages with the Hydrogen Image component
  • Generate sitemap.xml, robots.txt, and per-route meta correctly
  • Layer JSON-LD schemas onto Hydrogen routes without duplication

5.1 Hydrogen Image

The Shopify CDN supports on-the-fly image transformation. The Image component generates a responsive srcset, lazy loads by default, and uses aspectRatio to prevent layout shift.

import { Image } from "@shopify/hydrogen"; // Hero image (above the fold) — eager, high priority <Image data={hero.image} aspectRatio="16/9" sizes="100vw" loading="eager" fetchpriority="high" /> // Below-the-fold images — lazy, low priority (default) <Image data={product.image} aspectRatio="1/1" sizes="(min-width: 768px) 33vw, 100vw" />
LCP Rule

The single biggest LCP regression on Hydrogen storefronts: lazy-loading the hero. Always set loading="eager" AND fetchpriority="high" on the LCP image. Same rule as Liquid, different syntax.

5.2 Per-route meta tags

React Router v7 exports a meta function from each route. Hydrogen's getSeoMeta helper assembles a clean set including Open Graph and canonical URLs.

import { getSeoMeta } from "@shopify/hydrogen"; import type { Route } from "./+types/products.$handle"; export const meta: Route.MetaFunction = ({ data, location }) => { if (!data?.product) return []; return getSeoMeta({ title: `${'$'}{data.product.title} | Your Brand`, description: data.product.description?.slice(0, 155), url: `https://yourdomain.com${'$'}{location.pathname}`, media: data.product.featuredImage?.url, }); };

5.3 Sitemap and robots

// app/routes/[sitemap.xml].tsx import { getSitemap } from "@shopify/hydrogen"; import type { Route } from "./+types/[sitemap.xml]"; export async function loader({ request, context }: Route.LoaderArgs) { return getSitemap({ storefront: context.storefront, request, types: ["products", "collections", "pages", "articles"], }); } // app/routes/[robots.txt].tsx export async function loader() { return new Response( [ "User-agent: *", "Allow: /", "Sitemap: https://yourdomain.com/sitemap.xml", ].join("\n"), { headers: { "Content-Type": "text/plain" } } ); }

5.4 JSON-LD on Hydrogen routes

The getSeoMeta helper covers tags. Structured data goes into the route component as a script tag. Use the same ownership rules as Liquid: Organization in root.tsx, Product on PDP, BreadcrumbList on every route via a layout component.

// app/components/ProductSchema.tsx export function ProductSchema({ product }: { product: any }) { const schema = { "@context": "https://schema.org", "@type": "Product", name: product.title, description: product.description, image: product.featuredImage?.url, offers: { "@type": "Offer", price: product.priceRange.minVariantPrice.amount, priceCurrency: product.priceRange.minVariantPrice.currencyCode, availability: product.availableForSale ? "https://schema.org/InStock" : "https://schema.org/OutOfStock", }, }; return ( <script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(schema) }} /> ); }

5.5 Core Web Vitals targets

MetricTargetHydrogen lever
LCP< 1.5sEager hero image, server-rendered hero, edge cache
INP< 200msDefer non-critical JS, use React 18 concurrent features
CLS< 0.1Always set aspectRatio on Image; reserve space for fonts
TTFB< 200msEdge-cached loaders; avoid waterfall queries
MODULE 06 / 08

AI Integration: Wire the DDS Sovereign AGI Suite to Your Storefront

This is the module that justifies migrating to Hydrogen in 2026. The Storefront MCP turns your headless storefront into an addressable surface for AI agents. We will use the DDS Sovereign AGI Suite as the worked example, but the same pattern applies to any agent stack you operate.

You will leave this module having built:

  • A working Storefront MCP integration on Hydrogen 2026.4
  • Four agent-callable tools wired to the Sovereign Orchestrator Pro
  • An AI-powered product recommendation route using the suite

6.1 What Storefront MCP actually is

Model Context Protocol is the open standard Anthropic introduced for agent-to-tool communication. Shopify added a Storefront MCP server in the Winter '26 Edition. Hydrogen 2026 automatically proxies requests to /api/mcp to that server, exposing live commerce data (products, collections, cart, checkout) to any MCP-compatible agent.

Practically: an AI agent connected to https://your-storefront.com/api/mcp can query products, manipulate carts, and assist customers without any custom backend you have to write.

6.2 The Sovereign AGI Suite, reframed for headless

For the worked example, the four flagship synthetic employees from the DDS Sovereign Orchestrator map to four storefront integration points. Each employee's role is reframed below.

SOVEREIGN ORCHESTRATOR PRO V5.0

The conductor. Receives customer-intent prompts, decides which sub-agent should respond, dispatches the call to MCP, and returns a structured response.

Storefront role: /api/agent route handler

AGI-CORE-PRO V1.0

The reasoning layer. Handles ambiguous customer queries that require multi-turn clarification or product comparison synthesis.

Storefront role: search-by-intent endpoint

VIBETUBE AI V1.1.1

The content engine. Generates product-specific video scripts, descriptions, and structured marketing copy from the product graph.

Storefront role: PDP description enrichment

NICHE-FORGE-CORE V3.0

The targeting engine. Maps a visitor session to a niche persona and surfaces the right collection or product subset.

Storefront role: collection page personalization

6.3 Step 1: confirm Storefront MCP is enabled

In Hydrogen 2026.4, MCP proxying happens automatically as long as your server.ts uses createRequestHandler with a valid storefront in context. No additional configuration is required.

// server.ts (verify your handler looks like this) import { createRequestHandler } from "@shopify/hydrogen/oxygen"; import { createAppLoadContext } from "~/lib/context"; export default { async fetch(request, env, executionContext) { const handler = createRequestHandler({ build: await import("./build/server"), getLoadContext: () => createAppLoadContext(request, env, executionContext), // proxyStandardRoutes is removed in 2026.4 — proxy is always enabled }); return handler(request); }, };

Verify it works locally: curl http://localhost:3000/api/mcp/health should return a 200 with the MCP server identity. If you get a 404, your context is missing the storefront property.

6.4 Step 2: build the AGI gateway route

The Sovereign Orchestrator needs a single endpoint on the storefront that accepts a customer-intent prompt and routes it. This is the /api/agent route.

// app/routes/api.agent.tsx import type { Route } from "./+types/api.agent"; interface AgentRequest { prompt: string; sessionId: string; cartToken?: string; agent?: "orchestrator" | "agi-core" | "vibetube" | "niche-forge"; } export async function action({ request, context }: Route.ActionArgs) { if (request.method !== "POST") { return new Response("Method Not Allowed", { status: 405 }); } const { env } = context; const body: AgentRequest = await request.json(); const agent = body.agent ?? "orchestrator"; // Forward to your Sovereign Orchestrator endpoint. // The orchestrator decides which synthetic employee handles this. const orchestratorResponse = await fetch(env.SOVEREIGN_ORCHESTRATOR_URL, { method: "POST", headers: { "Authorization": `Bearer ${'$'}{env.SOVEREIGN_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ prompt: body.prompt, session_id: body.sessionId, // MCP endpoint the orchestrator will call back to: mcp_endpoint: `${'$'}{new URL(request.url).origin}/api/mcp`, mcp_auth: env.MCP_PROXY_AUTH_TOKEN, // for storefront-issued auth target_agent: agent, cart_context: body.cartToken ? await context.cart.get() : null, }), }); if (!orchestratorResponse.ok) { return Response.json( { error: "Orchestrator unavailable" }, { status: 502 } ); } const result = await orchestratorResponse.json(); return Response.json(result, { headers: { "Cache-Control": "private, no-store" }, }); }

6.5 Step 3: register the four MCP tools the orchestrator can call

Storefront MCP exposes a default tool set. You can extend it with your own tools by adding routes under /api/mcp/tools/. The four below match the four AGI Suite employees.

// app/routes/api.mcp.tools.search-by-intent.tsx (AGI-CORE-PRO target) import type { Route } from "./+types/api.mcp.tools.search-by-intent"; export async function action({ request, context }: Route.ActionArgs) { const { intent, niche, priceMax } = await request.json(); const { products } = await context.storefront.query(SEARCH_QUERY, { variables: { query: `${'$'}{intent} ${'$'}{niche ? `tag:${'$'}{niche}` : ""} ${'$'}{priceMax ? `price:<${'$'}{priceMax}` : ""}`, first: 10, }, cache: context.storefront.CacheShort(), }); return Response.json({ tool: "search-by-intent", results: products.nodes.map((p: any) => ({ handle: p.handle, title: p.title, price: p.priceRange.minVariantPrice, url: `/products/${'$'}{p.handle}`, })), }); }
// app/routes/api.mcp.tools.enrich-pdp.tsx (VIBETUBE AI target) import type { Route } from "./+types/api.mcp.tools.enrich-pdp"; export async function action({ request, context }: Route.ActionArgs) { const { handle } = await request.json(); const { product } = await context.storefront.query(PRODUCT_QUERY, { variables: { handle }, cache: context.storefront.CacheLong(), }); // Forward to VibeTube AI for content generation. const enriched = await fetch(context.env.VIBETUBE_AI_URL, { method: "POST", headers: { "Authorization": `Bearer ${'$'}{context.env.SOVEREIGN_API_KEY}` }, body: JSON.stringify({ product_data: product, output_types: ["video_script", "long_description", "social_caption"], }), }); return Response.json({ tool: "enrich-pdp", base_product: product, enriched_content: await enriched.json(), }); }
// app/routes/api.mcp.tools.personalize-collection.tsx (NICHE-FORGE-CORE target) import type { Route } from "./+types/api.mcp.tools.personalize-collection"; export async function action({ request, context }: Route.ActionArgs) { const { sessionId, collectionHandle } = await request.json(); // 1. Ask Niche-Forge for the visitor's persona vector const persona = await fetch(`${'$'}{context.env.NICHE_FORGE_URL}/persona`, { method: "POST", headers: { "Authorization": `Bearer ${'$'}{context.env.SOVEREIGN_API_KEY}` }, body: JSON.stringify({ session_id: sessionId }), }).then((r) => r.json()); // 2. Pull the collection const { collection } = await context.storefront.query(COLLECTION_QUERY, { variables: { handle: collectionHandle }, cache: context.storefront.CacheShort(), }); // 3. Re-rank server-side using Niche-Forge weights const reranked = collection.products.nodes.sort( (a: any, b: any) => scoreFor(b, persona) - scoreFor(a, persona) ); return Response.json({ tool: "personalize-collection", persona_tag: persona.dominant_niche, products: reranked.slice(0, 12), }); } function scoreFor(product: any, persona: any) { let score = 0; for (const tag of product.tags ?? []) { if (persona.weights?.[tag]) score += persona.weights[tag]; } return score; }
// app/routes/api.mcp.tools.cart-assist.tsx (Orchestrator target) import type { Route } from "./+types/api.mcp.tools.cart-assist"; export async function action({ request, context }: Route.ActionArgs) { const { intent, variantId, quantity } = await request.json(); const { cart } = context; switch (intent) { case "add": return Response.json(await cart.addLines([{ merchandiseId: variantId, quantity: quantity ?? 1 }])); case "remove": return Response.json(await cart.removeLines([variantId])); case "summary": return Response.json({ cart: await cart.get() }); default: return Response.json({ error: "Unknown cart intent" }, { status: 400 }); } }

6.6 Step 4: client-side hook that talks to the agent

// app/components/AgentChat.tsx import { useState } from "react"; export function AgentChat() { const [messages, setMessages] = useState<Array<{ role: string; text: string }>>([]); const [input, setInput] = useState(""); const [isThinking, setIsThinking] = useState(false); async function send() { if (!input.trim()) return; const userMsg = { role: "user", text: input }; setMessages((m) => [...m, userMsg]); setInput(""); setIsThinking(true); const res = await fetch("/api/agent", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ prompt: input, sessionId: getOrCreateSessionId(), }), }); const data = await res.json(); setMessages((m) => [...m, { role: "agent", text: data.response }]); setIsThinking(false); } return ( <div className="agent-chat"> <ul> {messages.map((m, i) => ( <li key={i} data-role={m.role}>{m.text}</li> ))} </ul> <input value={input} onChange={(e) => setInput(e.target.value)} onKeyDown={(e) => e.key === "Enter" && send()} placeholder="Ask the AGI Suite anything..." disabled={isThinking} /> <button onClick={send} disabled={isThinking}>Send</button> </div> ); } function getOrCreateSessionId() { let id = sessionStorage.getItem("agi-session"); if (!id) { id = crypto.randomUUID(); sessionStorage.setItem("agi-session", id); } return id; }

6.7 Step 5: orchestrator-side configuration

On the Sovereign Orchestrator side (running on your local Ollama instance, a hosted endpoint, or wherever your suite lives), register the storefront's MCP endpoint as a tool source. The configuration shape varies by orchestrator framework. The minimum the orchestrator needs:

{ "mcp_servers": [ { "name": "dds_storefront", "url": "https://your-hydrogen-storefront.com/api/mcp", "auth": { "type": "bearer", "token_env": "MCP_PROXY_AUTH_TOKEN" }, "tool_filter": [ "search-by-intent", "enrich-pdp", "personalize-collection", "cart-assist", "products.list", "products.get", "cart.get" ] } ] }
Production Note

This wiring is a worked example, not a turnkey integration with a public Sovereign AGI Suite endpoint. The DDS Sovereign AGI Suite is internal infrastructure. The pattern shown here is the same pattern any team would use to wire their own agent stack to a Hydrogen storefront via Storefront MCP. Treat the URLs and bearer tokens as placeholders for your equivalents.

6.8 Failure modes to plan for

  • MCP rate limiting: Storefront MCP shares the Storefront API rate budget. Cache aggressively on the agent side. Do not let an agent loop hammer products.list.
  • Stale cart context: Agents that operate on stale cart state will produce confusing UX. Always pass the live cart token to /api/agent and let the orchestrator re-fetch.
  • Tool ambiguity: Agents will sometimes pick search-by-intent when they should call products.get. Resolve this by giving each tool a sharper description in your tool registry, not by adding more tools.
  • Auth token rotation: The bearer token between orchestrator and storefront should rotate on a fixed schedule. Store it in Oxygen environment variables and version it.
MODULE 07 / 08

Deployment and Operations

Shipping a Hydrogen storefront is the easy part. Operating one without engineering support is where most teams underestimate the cost. This module covers the deploy path and the day-2 surface area.

You will leave this module able to:

  • Deploy to Oxygen via GitHub with preview environments
  • Configure custom domains and environment branching correctly
  • Run the upgrade path from any current Hydrogen version to the latest

7.1 Connect Oxygen to GitHub

From your Shopify admin, navigate to Hydrogen > Storefronts > Create storefront. Choose "Connect a GitHub repo" and authorize Shopify against the org or user that owns your Hydrogen project. Oxygen will create a deploy webhook automatically.

# From your Hydrogen project root git init git add . git commit -m "Initial Hydrogen 2026.4 commit" git branch -M main git remote add origin git@github.com:your-org/your-storefront.git git push -u origin main

Every push to main triggers a production deploy. Every push to any other branch produces a preview deployment with a unique URL.

7.2 Environment management

Oxygen exposes a UI for environment variables, scoped per branch. The minimum set you need:

VariableScopeNotes
SESSION_SECRETAll32+ char random; rotate annually
PUBLIC_STOREFRONT_API_TOKENAllFrom Shopify admin custom app
PUBLIC_STOREFRONT_IDAllShop handle
PUBLIC_STORE_DOMAINAllyour-shop.myshopify.com
PUBLIC_CUSTOMER_ACCOUNT_API_CLIENT_IDAllRequired for customer login
SOVEREIGN_API_KEYAllBearer token to your AGI suite
SOVEREIGN_ORCHESTRATOR_URLAllYour orchestrator endpoint
MCP_PROXY_AUTH_TOKENAllStorefront-side auth for MCP

7.3 Custom domain

Add the custom domain in Oxygen settings. Shopify provisions an SSL certificate via the same flow as standard Shopify domains. DNS pointing rules are documented in the storefront settings UI; the canonical pattern is a CNAME from shops.myshopify.com to your apex via Shopify-managed records.

7.4 Observability minimum

Oxygen ships request logs and basic deploy metrics. For real production observability, add at minimum:

  • Error tracking: Sentry or equivalent, instrumented in entry.server.tsx
  • RUM (real user metrics): Cloudflare Web Analytics or a third-party RUM
  • Uptime: External pinger on the homepage and a key API route every minute
  • Storefront API budget: Track query volume so you know when caching slips

7.5 The upgrade path

Hydrogen ships on a calendar versioning cadence (year.release.patch). The CLI's upgrade command applies codemods automatically.

# Always cut a branch first git checkout -b upgrade/hydrogen-2026.4 # Run the upgrade — applies codemods where possible npx shopify hydrogen upgrade # Manual cleanup checklist: # 1. grep for proxyStandardRoutes — delete every occurrence (removed in 2026.4) # 2. grep for @shopify/remix-oxygen — rewrite to @shopify/hydrogen/oxygen # 3. Verify package.json versions match the canonical set # 4. Run typecheck npm run typecheck # 5. Run a full local test npm run dev # 6. Push the branch and let Oxygen build a preview git push origin upgrade/hydrogen-2026.4 # 7. After preview passes, merge to main
Quarterly Discipline

Storefront API and Customer Account API versions update quarterly. Hydrogen pins to the current API version on each release. If you skip two consecutive Hydrogen releases, the upgrade gets meaningfully harder. Schedule the upgrade on the same week every quarter.

MODULE 08 / 08

When Liquid Wins: The DDS Case Study

Most masterclasses on Hydrogen sell migration. This one ends by telling you when not to migrate. Design Delight Studio runs on Atelier 3.4.0 Liquid in production. Here is the explicit reasoning.

You will leave this module with:

  • A decision tree that filters out bad headless migrations
  • A clear understanding of which Liquid-app dependencies break on Hydrogen
  • The DDS scoring on the eight-axis matrix from Module 1

8.1 The DDS profile

  • Solo founder, zero external engineering staff
  • ~6 SKUs across one storefront on Shopify Basic
  • Heavy dependency on Liquid-compatible apps for reviews, fulfillment integration, and analytics
  • Sustainable streetwear targeting niche communities (gaming, retro, science)
  • AI-first development methodology already mature on Liquid (the Vibe Coding playbook)

8.2 DDS score on the eight-axis matrix

AxisScoreReasoning
Team React skill3Architect-CEO is React-fluent but not engineering full-time
App ecosystem dependency1Several Liquid-only apps in the stack
Performance ceiling needs2Current Liquid mobile PageSpeed at the Basic-plan ceiling already
UX customization needs2Atelier extension via custom sections is sufficient
Engineering budget1Solo founder; no FTE engineers
Time-to-launch pressure2Brand is shipping continuously; no migration window
Content workflow tolerance1Theme editor is the content workflow
AI-agent strategy3Sovereign AGI Suite drives backend ops, not storefront UX (yet)

Total: 15 of 40. The matrix says: stay on Liquid. Reality matches the matrix.

8.3 The decision tree, condensed

  1. Do you have at least one full-time React engineer? If no, stop. Stay on Liquid.
  2. Are your three highest-value apps explicitly headless-compatible? If no, the migration cost includes rebuilding their integrations.
  3. Is your current Liquid PageSpeed already 80+? If yes, the performance argument for Hydrogen is weak.
  4. Does your roadmap include AI-agent UX (a chat surface, recommendation engines, voice commerce)? If yes, Storefront MCP becomes a real reason to migrate. This is the strongest pull-factor in 2026.
  5. Can you absorb six months of feature freeze during migration? If no, run a parallel pilot on a subset of pages instead.

8.4 The honest summary

Hydrogen is the right platform when your constraints justify the cost. For most brands in 2026, those constraints do not exist yet. The Storefront MCP integration covered in Module 6 is the single feature most likely to change that calculus over the next twelve months. Watch that feature, plan for it, but do not migrate before you need to.

When DDS Will Migrate

If and when DDS adds an in-storefront agent surface (a chat interface where customers talk to the Sovereign AGI Suite directly on the product page), the eight-axis score will jump. That is the trigger. Until then, Liquid is the correct architecture for this brand.

Applied Reference / DDS Case Study

Migration Reference: Your DDS Theme to Hydrogen.

This section is the practical bridge from masterclass theory to your actual codebase. It maps every Atelier 3.4.0 file, section, page, and integration in the DDS theme to its Hydrogen 2026.4 equivalent, with effort flags and known risks.

How to use this reference

This is the document you keep open in a second tab while doing the migration. Each subsection below maps a layer of your current theme to its Hydrogen target, marks the difficulty, and flags the integration risks. Effort flags use four levels:

Easy port directly, low risk. Medium requires code changes but pattern is clear. Hard requires architectural decisions. Blocked needs a vendor or DDS decision before proceeding.

~24
Custom Sections
~30+
Page Templates
10
JSON-LD Schemas
8
Migration Phases
A / ARCH

Theme architecture mapping

All files

The Atelier directory structure does not exist in Hydrogen. Templates, sections, snippets, layouts, and assets all collapse into the React file tree under app/. This table is the one-to-one mapping.

Atelier 3.4.0 file Hydrogen equivalent Effort Notes
layout/theme.liquid app/root.tsx Medium Global head, fonts, GA4, meta defaults, header and footer wrappers, top-level error boundary
layout/checkout.liquid (none required) Easy Shopify hosts checkout. Hydrogen redirects via cart helper
templates/index.json app/routes/_index.tsx Medium Homepage. Each Atelier section becomes a React component composed in this route
templates/product.json app/routes/products.$handle.tsx Medium PDP. Use Storefront API ProductFragment, variant selector, JSON-LD Product schema
templates/collection.json app/routes/collections.$handle.tsx Medium PLP. Use Pagination helper. Migrate Collection Hero Grid v4.2 logic
templates/cart.json app/routes/cart.tsx Medium Cart route with action handler. Add MERCHANDISE_LINE_TRANSFORMERS_RUN_ERROR handling
templates/article.json app/routes/blogs.$blog.$article.tsx Easy Article view with BlogPosting schema
templates/blog.json app/routes/blogs.$blog.tsx Easy Blog index with article list
templates/page.json (default) app/routes/pages.$handle.tsx Medium Generic page route. Pulls page.bodyHtml from Storefront API
templates/page.dva-*.json (~25) app/routes/pages.dva-*.tsx or content source Hard The masterclass pages are the biggest content migration. See subsection F
templates/search.json app/routes/search.tsx Easy Search results. Predictive search with debounced input
templates/404.json app/routes/$.tsx (catch-all) Easy 404 boundary. Best practice: try product/collection lookup before failing
templates/customers/login.liquid app/routes/account.login.tsx Easy OAuth via Customer Account API. No password handling on storefront
templates/customers/account.liquid app/routes/account._index.tsx Medium Account dashboard. Behind auth middleware
templates/customers/order.liquid app/routes/account.orders.$id.tsx Medium Order detail view
sections/*.liquid (~24 custom) app/components/*.tsx Hard Section-by-section rebuild. See subsection B
snippets/*.liquid app/components/*.tsx (smaller) Medium Reusable sub-components. JSON-LD snippets become hooks or shared schema utils
config/settings_schema.json Metafields or env vars Medium No theme editor on Hydrogen. Settings move to Shopify metaobjects or hardcoded config
config/settings_data.json (deleted) Easy Stored values move to env or metafields
locales/en.default.json i18n in createHydrogenContext Easy EN-US only currently. No translation work
assets/*.woff2 public/fonts/*.woff2 Easy Self-host preserved. Add @font-face in root.tsx
assets/*.css Inline + app/styles/global.css Medium Per-section CSS becomes scoped CSS modules or styled inline
assets/*.js React state/effects in components Hard No global vanilla JS. Each script becomes component-scoped React
assets/*.png|jpg|svg public/images/* Easy Static assets. Reference via /images/filename
B / SECTIONS

DDS section to React component mapping

~24 sections

Each Atelier section in the DDS theme becomes a React component. The schema settings become props with TypeScript types. The Liquid output becomes JSX. The scoped CSS travels with the component.

DDS section Hydrogen component Effort Migration notes
DDS Header v1.0 components/Header.tsx Medium Sticky behavior via CSS. Mobile menu becomes useState. Cart drawer becomes Sheet component
Announcement Bar components/AnnouncementBar.tsx Easy Dismissible via sessionStorage
Hero Genesis v3 components/Hero.tsx Medium Owns ClothingStore, FAQPage, CollectionPage, Person, speakable schemas. Move schemas into route's handle pattern
Trust Bar v2 components/TrustBar.tsx Easy Static badges. No state
Showcase v3 components/Showcase.tsx Medium Featured products. Loader pattern, not Liquid section settings
Social Proof components/SocialProof.tsx Medium If wired to Judge.me, depends on review API headless compat
Organic Quiz v2 components/Quiz.tsx + routes/api.quiz.tsx Hard State machine becomes useReducer. Form submission goes to action route. Outcome routing via redirect
Platform Links components/PlatformLinks.tsx Easy Static social links. No state
Email Capture v2 components/EmailCapture.tsx + action route Medium Form submits to action that calls Mailchimp or whichever ESP API. CAPTCHA via Cloudflare Turnstile
SEO Manager v6.1 components/seo/*.tsx + route handle Hard Owns ItemList, SiteNavigationElement, HowTo, DefinedTermSet, Brand, OfferShippingDetails, MerchantReturnPolicy. Each becomes a hook returning a JSON-LD object. See subsection C
Email Popup v1.1 components/EmailPopup.tsx Medium Trigger logic (time/scroll/exit-intent) via useEffect. Suppression via cookie
Footer components/Footer.tsx Easy Static link groups. Menu data from Storefront API menu query
Collection Hero Grid v4.2 components/CollectionHeroGrid.tsx Hard 1,009 LOC of Liquid plus 75 schema settings becomes typed props. The 5 title styles become a discriminated union. Green aura animation ports to CSS keyframes
Collection Hero v2.2 components/CollectionHero.tsx Medium Standard collection banner pattern
Collection SEO Optimizer v2.2.0 components/seo/CollectionSeoSchema.tsx Medium Becomes a JSON-LD-emitting component called from collection route

Component skeleton: Header.tsx

Reference shape for migrating any DDS section. The schema settings become typed props, the Liquid logic becomes JSX, the scoped CSS travels in a CSS module.

// app/components/Header.tsx import { Link, NavLink } from "react-router"; import { useState } from "react"; import { CartCount } from "./CartCount"; interface HeaderProps { shopName: string; menu: { items: Array<{ id: string; title: string; url: string }> }; announcementText?: string; } export function Header({ shopName, menu, announcementText }: HeaderProps) { const [mobileOpen, setMobileOpen] = useState(false); return ( <header className="dds-header" data-mobile-open={mobileOpen}> {announcementText && ( <div className="dds-header-announcement">{announcementText}</div> )} <div className="dds-header-bar"> <Link to="/" className="dds-header-logo" aria-label={shopName}> {/* logo SVG */} </Link> <nav className="dds-header-nav" aria-label="Primary"> {menu.items.map((item) => ( <NavLink key={item.id} to={item.url}>{item.title}</NavLink> ))} </nav> <div className="dds-header-actions"> <Link to="/account" aria-label="Account">{/* icon */}</Link> <Link to="/search" aria-label="Search">{/* icon */}</Link> <Link to="/cart" aria-label="Cart"> <CartCount /> </Link> <button className="dds-header-toggle" onClick={() => setMobileOpen(!mobileOpen)} aria-expanded={mobileOpen} aria-controls="dds-mobile-menu" > {/* hamburger icon */} </button> </div> </div> </header> ); }
C / SEO

JSON-LD schema ownership migration

Critical for SEO continuity

The DDS schema architecture has strict single-owner rules. The Hydrogen migration must preserve the same ownership model or rich-result coverage will regress.

Schema type Current DDS owner Hydrogen owner Effort
Organizationtheme.liquidapp/root.tsxEasy
WebSite + SearchActiontheme.liquidapp/root.tsxEasy
BreadcrumbListtheme.liquidcomponents/Breadcrumbs.tsxMedium
ClothingStoreHero Genesis v3routes/_index.tsxEasy
FAQPage (homepage)Hero Genesis v3routes/_index.tsxEasy
CollectionPageHero Genesis v3routes/_index.tsxEasy
Person (founder)Hero Genesis v3routes/_index.tsxEasy
SpeakableSpecificationHero Genesis v3routes/_index.tsxEasy
ItemList (sitewide)SEO Manager v6.1app/root.tsxMedium
SiteNavigationElementSEO Manager v6.1app/root.tsxMedium
HowToSEO Manager v6.1app/root.tsxMedium
DefinedTermSetSEO Manager v6.1app/root.tsxMedium
BrandSEO Manager v6.1app/root.tsxEasy
OfferShippingDetailsSEO Manager v6.1routes/products.$handle.tsxMedium
MerchantReturnPolicySEO Manager v6.1routes/products.$handle.tsxMedium
Productmain-product.liquidroutes/products.$handle.tsxEasy
Article / BlogPostingmain-article.liquidroutes/blogs.$blog.$article.tsxEasy
Per-page FAQ/HowTo/VideoObjectIndividual page.liquid filesPer-route component or handle exportMedium

Pattern: schema-emitting hook

Every owned schema becomes a typed function returning a JSON-LD object. The route imports and emits it.

// app/lib/schemas/organization.ts export function organizationSchema(shopUrl: string) { return { "@context": "https://schema.org", "@type": "Organization", "@id": `${'$'}{shopUrl}/#organization`, "name": "Design Delight Studio", "alternateName": "DDS", "url": shopUrl, "logo": { "@type": "ImageObject", "url": `${'$'}{shopUrl}/images/dds-logo.png`, "width": 512, "height": 512 }, "founder": { "@type": "Person", "name": "Robert McCullock", "jobTitle": "Founder and Architect-CEO" }, "address": { "@type": "PostalAddress", "addressLocality": "Boston", "addressRegion": "MA", "addressCountry": "US" } }; } // app/root.tsx import { organizationSchema } from "~/lib/schemas/organization"; import { websiteSchema } from "~/lib/schemas/website"; export default function App() { const { shopUrl } = useLoaderData<typeof loader>(); return ( <html> <head> <script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(organizationSchema(shopUrl)) }} /> <script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(websiteSchema(shopUrl)) }} /> </head> <body>{/* ... */}</body> </html> ); }
SEO Continuity Risk

If schemas are emitted with different @id values during migration, Google may treat the site as new and rich-result coverage can drop temporarily. Preserve every @id string from the current DDS implementation. Use the existing https://ddsboston.com/#organization pattern verbatim.

D / PAGES

DDS page template inventory

~30+ custom pages

The DDS theme has many custom page.{handle}.liquid templates. Each becomes a Hydrogen route. The grouping below sorts them by migration priority.

Page family Hydrogen route Effort Notes
About Us (~1,735 LOC) routes/pages.about.tsx Medium Long-form content. Decide content source: MDX in repo vs Storefront API page.bodyHtml
Contact Us routes/pages.contact.tsx Easy Form submits to action route. Anti-solicitation notice preserved as static copy
FAQ (18 Q&As, 6 schemas) routes/pages.faq.tsx Medium Accordion as React state. FAQPage schema in route handle
Shipping policy + 4 policy pages routes/policies.$handle.tsx Easy Use Hydrogen's Policy query for stock policies; static for custom
DDS Vibe Academy hub routes/pages.dds-vibe-academy.tsx Hard 25-masterclass index. Decide: hardcoded list, metaobject collection, or filesystem-driven
DVA masterclass pages (~25) routes/pages.dva-$slug.tsx Hard The biggest content chunk. See subsection F
Investor pitch (v15.0) routes/pages.investor-pitch.tsx Hard Long-form, schema-heavy. Same migration shape as DVA pages
Portfolio / resume (v14.0) routes/pages.portfolio.tsx Hard Sovereign Terminal aesthetic ports cleanly to React
VibeTube AI landing (V3) routes/pages.vibetube-ai.tsx Medium VideoObject schema. Static product page-style
Sovereign Synthetic Empire routes/pages.synthetic-empire.tsx Medium 14-employee showcase. Static data + animation
Atelier OS / Synthetic Director / NicheForge / Content Orchestrator routes/pages.{handle}.tsx Medium Product-suite landing pages. Same migration shape
Legend of the Red Dragon 2026 routes/pages.lord-2026.tsx Medium Includes mature content warning gate. Use a state-based reveal
MTG Judge gated collection routes/collections.mtg-judge.tsx with middleware gate Hard Auth gate moves to middleware. Verification flow on a separate route
B2B Chelsea Chamber order form routes/pages.b2b-chelsea.tsx + action route Medium Form submits to action that creates a draft order via Admin API (separate concern)
Sitemap (Spring 2026 v3.0.0) routes/[sitemap.xml].tsx Easy Use getSitemap() from Hydrogen. Static custom-page sitemap merges via XML splice if needed
E / ASSETS

Asset migration

Fonts, images, icons, scripts

Fonts (Playfair Display + DM Sans)

Self-hosted WOFF2 must be preserved. No Google Fonts. No external preconnects. The font-face declarations move from a CSS asset into the root component.

/* app/styles/fonts.css (imported in root.tsx) */ @font-face { font-family: "Playfair Display"; font-weight: 400 700; font-style: normal; font-display: swap; src: url("/fonts/playfair-display-variable.woff2") format("woff2-variations"); } @font-face { font-family: "DM Sans"; font-weight: 400 700; font-style: normal; font-display: swap; src: url("/fonts/dm-sans-variable.woff2") format("woff2-variations"); }

Images

Two paths depending on source.

  • Product / collection / variant images: Already on Shopify CDN. Use Hydrogen <Image> with the Storefront API URL. Zero migration work.
  • Theme assets (logos, icons, hero backgrounds): Move from assets/*.png to public/images/*. Reference via /images/filename.

Icons

If you currently use inline SVG snippets, they port directly as React components. If you use an icon library (Lucide, Heroicons, custom SVG sprites), keep the same library on Hydrogen.

JavaScript assets

Every assets/*.js file becomes component-scoped React. Common patterns:

  • Scroll-spy or sticky behavior: useEffect with IntersectionObserver
  • Form submission with optimistic UI: useFetcher from React Router
  • Dropdown / accordion / modal: useState for open state
  • Carousel / slider: keep the same library (Embla, Swiper) installed via npm
jQuery

The DDS skill prohibits jQuery on the current theme already, so this is a non-issue. If any vendor snippet sneaks jQuery in, it must be replaced with vanilla DOM or React state during migration.

F / CONTENT

Content migration strategy

25 masterclasses + ~10 long-form pages

The DDS theme has roughly 35 long-form pages with deep custom Liquid markup. This is the single largest migration effort. Three viable strategies, each with different tradeoffs.

Strategy Storage Editing workflow Pros / Cons
1. Hardcoded React JSX in routes/ directly Code edits + redeploy Pro: maximum control, full type safety. Con: no non-developer editing; every copy change is a deploy
2. MDX in repo .mdx files in content/ Markdown edits + redeploy Pro: cleaner than JSX for prose; can embed React. Con: still requires git workflow; no admin UI
3. Storefront API pages Shopify admin Pages Shopify admin rich-text editor Pro: edit in admin, no deploy. Con: cannot embed custom React components inside the body. Loses the SEO Magnet System V2 advantages

Recommended hybrid

  • DVA masterclass pages stay as code (Strategy 1). They are the brand's flagship content; the schema density and custom components justify code.
  • Investor pitch / Portfolio / Synthetic Empire stay as code. Same reasoning.
  • Policy pages use the Hydrogen Policy query (no migration work).
  • About Us, Contact, FAQ stay as code with minimal logic. Easy migration.
  • Future blog-style content goes through Storefront API blog if marketers need to edit.

The DVA hub migration pattern

Each masterclass has its own route file. The hub generates the index from a typed manifest, not a hardcoded list. New masterclasses get one entry in the manifest plus their route file.

// app/lib/dva-manifest.ts export interface MasterclassEntry { slug: string; title: string; description: string; publishedDate: string; status: "live" | "coming-soon" | "archived"; category: "ai-tools" | "headless" | "shopify" | "fundamentals"; estimatedHours: number; } export const DVA_MASTERCLASSES: MasterclassEntry[] = [ { slug: "hydrogen-2026", title: "Shopify Hydrogen 2026 Masterclass", description: "Headless storefronts with React Router v7, Oxygen, and Storefront MCP.", publishedDate: "2026-04-25", status: "live", category: "headless", estimatedHours: 8, }, { slug: "claude-code", title: "Claude Code Masterclass", description: "Add description from your existing page.", publishedDate: "2026-03-15", // verify against your actual published date status: "live", category: "ai-tools", estimatedHours: 4, }, // ... one entry per existing masterclass ]; // app/routes/pages.dds-vibe-academy.tsx import { DVA_MASTERCLASSES } from "~/lib/dva-manifest"; export async function loader() { return { classes: DVA_MASTERCLASSES.filter((c) => c.status === "live") }; } export default function Hub({ loaderData }: Route.ComponentProps) { return ( <ul> {loaderData.classes.map((c) => ( <li key={c.slug}> <a href={`/pages/dva-${'$'}{c.slug}`}>{c.title}</a> <p>{c.description}</p> </li> ))} </ul> ); }
G / INTEGRATIONS

Third-party integration migration

Apps, analytics, fulfillment

This is the section where most headless migrations stall. Each external integration must be verified against headless compatibility before migration begins.

Integration Current path Hydrogen path Effort
GA4 (G-BRJLYR4558) Direct gtag.js in theme.liquid Inline script in root.tsx + Hydrogen analytics provider Easy
GTM (GTM-NDB6VD5J) Dual deployment with GA4 Decision required: keep GTM or consolidate to GA4 only Medium
Customer privacy / consent Shopify standard banner ShopifyCustomerPrivacy component (Hydrogen built-in) Easy
Judge.me reviews Liquid theme app extension Verify Judge.me headless compatibility before committing Blocked
Printful POD Shopify-side fulfillment No theme touchpoint. Zero migration work Easy
X.com posting (App 29889820) External (not theme-bound) No theme touchpoint. Zero migration work Easy
Storefront MCP / AGI Suite Not currently wired New Module-6 integration Medium
Email service provider Email Capture v2 form action Hydrogen action route calling ESP API Medium
Other Liquid-only apps Theme app extensions Verify each app's headless docs individually Blocked
Critical pre-migration step

Before scheduling any migration work, run the headless compatibility audit on every installed app. Open Settings > Apps > Apps and integrations and list every active app. For each one, search the app's documentation for "headless" or "Storefront API". If any app you depend on has no headless path, that is either a custom integration cost or a reason to stay on Liquid for that feature.

GA4 + Hydrogen analytics provider

// app/root.tsx (relevant portion) import { Analytics, useShopAnalytics } from "@shopify/hydrogen"; export default function App() { const { shop } = useLoaderData<typeof loader>(); return ( <html> <head> {/* GA4 gtag.js */} <script async src="https://www.googletagmanager.com/gtag/js?id=G-BRJLYR4558" /> <script dangerouslySetInnerHTML={{ __html: ` window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'G-BRJLYR4558'); `, }} /> </head> <body> <Analytics.Provider shop={shop} consent={{ checkoutDomain: "ddsboston.com" }}> <Outlet /> </Analytics.Provider> </body> </html> ); }
H / SEO

SEO continuity plan

Preserve rankings during migration

A botched headless migration can drop organic traffic by 30 to 60 percent for two to four months. The plan below treats SEO continuity as a hard constraint, not an afterthought.

URL preservation

Every URL in the current sitemap must resolve in Hydrogen at the same path. The Hydrogen route file naming aligns naturally with Shopify URL patterns:

  • /products/{handle}routes/products.$handle.tsx
  • /collections/{handle}routes/collections.$handle.tsx
  • /blogs/{blog}/{article}routes/blogs.$blog.$article.tsx
  • /pages/{handle}routes/pages.$handle.tsx
  • /accountroutes/account._index.tsx
  • /policies/{handle}routes/policies.$handle.tsx

Redirect map

If any URL changes during migration, log it. The redirect map ships as a file in the repo and a 301 in middleware.

// app/lib/redirects.ts export const REDIRECT_MAP: Record<string, string> = { // "/old-path": "/new-path", // Add entries only if the URL must change. Goal: empty map. }; // app/root.tsx middleware (or in server.ts) import { REDIRECT_MAP } from "~/lib/redirects"; export const middleware = [ ({ request }, next) => { const url = new URL(request.url); const target = REDIRECT_MAP[url.pathname]; if (target) { return Response.redirect(`${'$'}{url.origin}${'$'}{target}`, 301); } return next(); }, ];

Canonical URLs

Every route must export a canonical via getSeoMeta with the full production URL. Strip tracking parameters but preserve variant selectors and pagination.

Sitemap continuity

The current Spring 2026 v3.0.0 sitemap structure is custom. Hydrogen's getSitemap() generates a default sitemap; merge custom-page entries by extending it.

// app/routes/[sitemap.xml].tsx import { getSitemap } from "@shopify/hydrogen"; import { DVA_MASTERCLASSES } from "~/lib/dva-manifest"; export async function loader({ request, context }: Route.LoaderArgs) { const baseSitemap = await getSitemap({ storefront: context.storefront, request, types: ["products", "collections", "pages", "articles"], }); // Append custom DVA pages and any other static routes const customUrls = DVA_MASTERCLASSES .filter((c) => c.status === "live") .map((c) => ({ loc: `https://ddsboston.com/pages/dva-${'$'}{c.slug}`, lastmod: c.publishedDate, priority: 0.8, })); // Merge into the base XML (parse, append <url> nodes, re-serialize) return mergeSitemap(baseSitemap, customUrls); }

Pre-cutover SEO checklist

  • Crawl the current Liquid site with Screaming Frog or equivalent. Save the URL inventory
  • Crawl the staging Hydrogen site. Compare URL coverage to the inventory. Zero gaps allowed
  • Submit the new sitemap in Search Console, but do not point production DNS yet
  • Validate every JSON-LD block on every route via Rich Results Test
  • Verify canonical URLs match the production domain on every page
  • Verify robots.txt allows production crawlers and blocks staging
I / ROADMAP

Phased migration roadmap

8 phases, sequential

Do not attempt a big-bang migration. The plan below ships value at the end of each phase and keeps the Liquid site live until cutover.

  1. Phase 1: Foundation — scaffold Hydrogen 2026.4 starter, configure Oxygen, wire Storefront API tokens, deploy a hello-world. Build root.tsx with global concerns: fonts, GA4, Organization/WebSite/BreadcrumbList schemas, header/footer scaffolds. Effort estimate: 1-2 weeks.
  2. Phase 2: Critical commerce path — home, product detail, collection, cart, checkout redirect. This is the path that drives revenue. Test against staging Storefront API. Effort estimate: 2-3 weeks.
  3. Phase 3: Customer account — login, authorize, logout, account dashboard, order history. Behind auth middleware. Effort estimate: 1 week.
  4. Phase 4: Search and discovery — search route, 404 boundary, blog index, article view. Effort estimate: 1 week.
  5. Phase 5: Content pages — About, Contact, FAQ, policies, then DVA hub and the 25 masterclass pages. This is the longest phase. Effort estimate: 4-8 weeks.
  6. Phase 6: Apps and integrations — verify each app's headless path, migrate or replace blocked ones, wire ESP, replace any Liquid-only feature. Effort estimate: 2-3 weeks (variable).
  7. Phase 7: AI integration — verify Storefront MCP, build /api/agent gateway, register the four custom MCP tools, wire the AGI Suite. Module 6 implementation. Effort estimate: 2 weeks.
  8. Phase 8: Cutover — final sitemap audit, redirect map verification, DNS update, monitoring intensified for 30 days. Keep Liquid theme published as a rollback option. Effort estimate: 1 week + 30-day monitoring.
Total Estimate

Sequential execution at solo-founder pace: roughly 16-26 weeks of focused engineering. Parallelizing some phases is possible (Phase 5 content can begin during Phase 2). Solo-founder reality: this is a 4-6 month project minimum even with AI-first development.

J / RISKS

Risk register

Pre-empt the predictable failures

The risks below are the ones that bite real headless migrations. Each lists a mitigation that should be planned before the relevant phase begins.

Risk 1: App incompatibility surfaces mid-migration

An app you assumed would work headlessly turns out to require Liquid. Mitigation: complete the app inventory and headless audit in Phase 1, before writing any production component code. If a critical app has no headless path, decide before Phase 2 whether to replace, custom-build, or abort.

Risk 2: SEO regression at cutover

Schemas with different @id values or URLs that resolve differently cause Google to re-evaluate the site. Mitigation: preserve every @id string, run pre-cutover SEO checklist, keep Liquid theme published for instant rollback during the first 30 days.

Risk 3: Cart Transform Function breakage

If DDS uses any Cart Transform Functions, the new MERCHANDISE_LINE_TRANSFORMERS_RUN_ERROR error code in 2026-04 must be handled explicitly or the cart will appear broken. Mitigation: test every Cart Transform Function on staging Hydrogen before cutover.

Risk 4: Long-form content velocity drops

If you currently iterate masterclass pages quickly via the Liquid theme editor, the Hydrogen workflow (code edit + commit + deploy) is slower per-edit. Mitigation: prepare a fast local dev loop, set up branch-based preview deployments, accept that minor copy edits will be commits not theme-editor saves.

Risk 5: AI agent budget consumption

Once Storefront MCP is wired and agents can hit /api/agent, an unmonitored loop can burn Storefront API rate budget fast. Mitigation: rate limiting from day one (Prompt 36), structured logging (Prompt 37), agent-side caching of frequent queries.

Risk 6: DNS cutover during peak traffic

A Friday-night DNS flip with an issue means a weekend of degraded performance. Mitigation: cutover on a low-traffic Tuesday morning, with Liquid theme still published, monitoring dashboards live.

Risk 7: Solo-founder burnout

16-26 weeks of focused engineering on top of running DDS operations. Mitigation: be honest about whether the migration's value justifies the opportunity cost. The Module 1 decision matrix exists for exactly this reason. If the score has not changed since the baseline, do not start.

K / GATES

Three decision gates before starting

Answer all three first

Before scheduling Phase 1, three decisions must be made. None of these can be deferred without re-work later.

  1. Gate 1: Headless app audit complete? Every active app on the DDS Shopify admin has been verified for headless compatibility, and any blocker has a mitigation plan. If no, do not start.
  2. Gate 2: Content storage strategy decided? The hybrid recommendation in subsection F (code for flagship pages, Storefront API for blog) is committed to. The DVA manifest pattern is approved. If no, do not start.
  3. Gate 3: Migration trigger event identified? The Module 1 decision matrix score has crossed the threshold (29+ of 40), or a specific business event (in-storefront agent surface, redesign, replatform) justifies the migration cost. If no, do not start. Stay on Liquid until the trigger fires.
Honest framing

This entire migration reference is a planning artifact, not a recommendation to migrate. The most defensible version of this masterclass acknowledges that the right answer for DDS today is to stay on Atelier 3.4.0 Liquid until one of the three gates above flips. When that happens, this reference becomes the execution plan.

Vibe Coding Prompt Library

50 paste-ready prompts for Hydrogen 2026.4.

Calibrated for Claude Code, Antigravity, and Cursor. Each prompt is scoped tight enough to produce working code on the first try when paired with the CLAUDE.md from Module 2.

PROMPT 01SETUP

Scaffold a fresh Hydrogen 2026.4 TypeScript project, then update package.json to use the canonical version pinning from the masterclass. Run typecheck and report any errors.

PROMPT 02SETUP

Create a .env.local file with placeholder values for all six Storefront and Customer Account API variables. Add an .env.example committed to git with the same keys but no values.

PROMPT 03SETUP

Generate a CLAUDE.md at the project root that documents file conventions, caching rules, type-safety rules, and forbidden patterns specific to this Hydrogen 2026 project.

PROMPT 04SETUP

Audit my server.ts for any usage of the deprecated proxyStandardRoutes option. If found, remove it and confirm the request handler still compiles.

PROMPT 05SETUP

Grep my codebase for any imports from @shopify/remix-oxygen and rewrite them to @shopify/hydrogen/oxygen. Show me a diff before applying changes.

PROMPT 06ARCH

Build app/lib/context.ts using createHydrogenContext with full TypeScript types. Include cart, customerAccount, storefront, session, and i18n. Add a stub for the Sovereign API key in env.

PROMPT 07ARCH

Create app/lib/fragments.ts with fragments for Product, Variant, Collection, Cart, and Customer. Use canonical naming and include all fields needed for a standard storefront.

PROMPT 08ARCH

Add middleware to app/routes/account.tsx that redirects unauthenticated users to /account/login and applies private no-store cache headers to authenticated responses.

PROMPT 09ARCH

Audit every loader in app/routes for cache strategy correctness. Flag any loader using CacheLong on a query that includes buyerIdentity, cartId, or customerAccessToken.

PROMPT 10ARCH

Refactor the slow recommendations query on the product detail route to use defer + Suspense + Await so it does not block the initial render.

PROMPT 11ROUTE

Build app/routes/_index.tsx with a hero featured collection, three featured products, and a deferred recommended-collections section. Use streaming SSR.

PROMPT 12ROUTE

Build app/routes/products.$handle.tsx with full variant selector, optimistic add-to-cart, JSON-LD Product schema, and proper meta tags via getSeoMeta.

PROMPT 13ROUTE

Build app/routes/collections.$handle.tsx using the Pagination helper for cursor-based pagination. Include a sort-by control and filter-by-tag UI.

PROMPT 14ROUTE

Build app/routes/cart.tsx with action handlers for LinesAdd, LinesUpdate, LinesRemove, and BuyerIdentityUpdate. Add explicit handling for MERCHANDISE_LINE_TRANSFORMERS_RUN_ERROR.

PROMPT 15ROUTE

Build the three Customer Account API routes: account.login.tsx, account.authorize.tsx, account.logout.tsx. Verify the OAuth callback URL is correctly registered in the admin.

PROMPT 16ROUTE

Build app/routes/account.orders.tsx that lists the logged-in customer's order history with deferred loading and graceful empty states.

PROMPT 17ROUTE

Build a search route at app/routes/search.tsx that uses the Storefront API predictive search with debouncing and CacheShort.

PROMPT 18ROUTE

Build app/routes/[sitemap.xml].tsx using getSitemap and app/routes/[robots.txt].tsx with a static response. Verify sitemap loads in production.

PROMPT 19ROUTE

Add a 404 boundary at app/routes/$.tsx that queries the Storefront API for the requested handle in case the URL is a valid product or collection.

PROMPT 20ROUTE

Build app/routes/blogs.$handle.$articleHandle.tsx for a single article view. Include Article JSON-LD and OG tags via getSeoMeta.

PROMPT 21PERF

Audit every Image component in app/components for missing aspectRatio, missing sizes, or missing fetchpriority on the LCP element. Report violations with file paths.

PROMPT 22PERF

Replace any client-side fetch to the Storefront API with a loader-based pattern. Identify candidates by grepping for fetch('/api/) in app/components.

PROMPT 23PERF

Configure the build to output a bundle analyzer report. Identify the three largest dependencies and propose code-splitting strategies for each.

PROMPT 24PERF

Set up a synthetic Lighthouse CI run on every PR. Block merges that regress LCP by more than 10 percent or CLS by more than 0.05.

PROMPT 25PERF

Add resource hints to root.tsx: preconnect to the Shopify CDN, dns-prefetch to the Customer Account API domain. Verify they fire on the Network tab.

PROMPT 26SEO

Add Organization, WebSite, and BreadcrumbList JSON-LD to root.tsx exactly once each. Audit for duplicates across child routes.

PROMPT 27SEO

Add a Product JSON-LD to products.$handle.tsx with offers, availability, brand, and aggregateRating if review data is available.

PROMPT 28SEO

Verify every route exports a meta function via getSeoMeta. Flag any route with missing or hardcoded meta tags.

PROMPT 29SEO

Add canonical URL handling that strips tracking parameters (utm_, fbclid, gclid) but preserves variant selectors and pagination.

PROMPT 30SEO

Generate hreflang tags for all locales the storefront supports. Wire them to the i18n config in createHydrogenContext.

PROMPT 31MCP

Verify Storefront MCP is enabled by curling /api/mcp/health. If 404, walk through the createRequestHandler context to find the missing storefront property.

PROMPT 32MCP

Build app/routes/api.agent.tsx as the Sovereign Orchestrator gateway. Forward requests to SOVEREIGN_ORCHESTRATOR_URL with the storefront's MCP endpoint as a callback.

PROMPT 33MCP

Build the four custom MCP tool routes: search-by-intent, enrich-pdp, personalize-collection, cart-assist. Each should validate input and return structured JSON.

PROMPT 34MCP

Build the AgentChat React component with optimistic UI, session persistence via sessionStorage, and a graceful disabled state during agent thinking.

PROMPT 35MCP

Add bearer-token verification middleware to /api/mcp/tools/* routes that rejects any request without a valid MCP_PROXY_AUTH_TOKEN header.

PROMPT 36MCP

Implement rate limiting on /api/agent that allows 10 requests per session per minute. Return 429 with a Retry-After header when exceeded.

PROMPT 37MCP

Add structured logging to every agent call: session_id, agent_target, prompt_length, response_time_ms, tool_calls_made. Pipe to Oxygen logs.

PROMPT 38MCP

Build a debug panel at /admin/agent-debug (gated behind a feature flag) that replays the last 50 agent calls with their tool sequences and responses.

PROMPT 39MCP

Wire NICHE-FORGE-CORE to the homepage so the featured collection re-ranks based on the visitor's persona vector. Cache the persona for 15 minutes per session.

PROMPT 40MCP

Add a fallback path so if SOVEREIGN_ORCHESTRATOR_URL is unreachable, the storefront falls back to a static product recommendation list without breaking the page.

PROMPT 41DEPLOY

Configure the GitHub repository with a CODEOWNERS file, branch protection on main, and required CI checks for typecheck and lint.

PROMPT 42DEPLOY

Set up Sentry instrumentation in entry.server.tsx and entry.client.tsx with release tagging tied to the git SHA from the Oxygen build.

PROMPT 43DEPLOY

Audit Oxygen environment variables across production, staging, and preview. Flag any variable defined in production but missing in staging.

PROMPT 44DEPLOY

Run the upgrade command for the latest Hydrogen release, then walk the codemod diff and explain every breaking change introduced.

PROMPT 45DEPLOY

Add a deployment health check route at /api/health that verifies Storefront API connectivity, MCP availability, and Sovereign Orchestrator reachability.

PROMPT 46QA

Generate Playwright end-to-end tests for the critical-path: home, PDP variant select, add to cart, view cart, begin checkout. Run them on every PR.

PROMPT 47QA

Audit every form in the storefront for keyboard accessibility, visible focus rings, and proper labels. Report violations against WCAG 2.1 AA.

PROMPT 48QA

Add a visual regression test suite with Percy or Chromatic for the homepage, PDP, PLP, and cart at three viewport widths: 375, 768, and 1440.

PROMPT 49QA

Run the full JSON-LD output of every route through Google's Rich Results Test. Report any errors or warnings for me to triage.

PROMPT 50QA

Generate a release note draft based on the diff between the last two Hydrogen versions in package-lock.json. Highlight breaking changes for the team.

Honest Comparison

Hydrogen vs Liquid vs Next.js vs Nuxt.

The four practical options for a Shopify-backed storefront in 2026. There is no single winner. The right answer depends on team shape and constraint set.

Comparison of the four practical Shopify-backed storefront stacks in 2026: Liquid, Hydrogen, Next.js, and Nuxt.
Dimension Liquid (Atelier) Hydrogen 2026 Next.js + SF API Nuxt + SF API
HostingShopify (managed)Oxygen (free)Vercel/AWS (paid)Vercel/Netlify (paid)
FrameworkLiquid templatesReact Router v7Next.js App RouterNuxt 3
Time to first shipDaysWeeksWeeks to monthsWeeks to months
App ecosystemFullPartialPartialPartial
Visual editingTheme editorNone nativeNone nativeNone native
Performance ceilingPlan-boundEdge sub-secondEdge sub-secondEdge sub-second
Storefront MCPNo native pathNative /api/mcpManual proxyManual proxy
Engineering needLowHighHighHigh
Best forMost brandsShopify-committed brands with React teamsMulti-backend or content-heavyVue-first teams
Pre-Launch Audit

30-item production checklist.

Run this against your storefront before pointing the production domain. Every item maps to a real failure mode someone has shipped to production.

  • SetupHydrogen pinned to 2026.4.0 in package.json
  • SetupStorefront API token has only required scopes
  • SetupCustomer Account API redirect URLs registered in admin
  • SetupSESSION_SECRET is 32+ chars and rotated
  • SetupAll required env vars present in Oxygen production
  • RoutesEvery route exports meta via getSeoMeta
  • Routes404 boundary attempts product/collection lookup before failing
  • RoutesCart action handles MERCHANDISE_LINE_TRANSFORMERS_RUN_ERROR
  • RoutesAuthenticated routes enforce middleware redirects
  • Routessitemap.xml and robots.txt return 200
  • PerfLCP image has loading eager and fetchpriority high
  • PerfEvery Image component has aspectRatio set
  • PerfNo client-side fetch to Storefront API
  • PerfSlow queries wrapped in defer + Await
  • PerfBundle size under 200KB initial JS
  • CacheNo CacheLong on buyer-specific queries
  • CacheAuthenticated routes return private no-store
  • SEOJSON-LD validates in Rich Results Test
  • SEONo duplicate Organization or WebSite schemas
  • SEOCanonical URLs strip tracking params
  • SEOOG and Twitter tags on every public route
  • A11yAll forms keyboard accessible with visible focus
  • A11yColor contrast 4.5:1 minimum for body text
  • A11yAll images have meaningful alt text
  • MCP/api/mcp/health returns 200
  • MCPCustom MCP tools require bearer auth
  • MCP/api/agent has rate limiting
  • OpsSentry instrumented client and server
  • OpsUptime monitor on homepage and /api/health
  • OpsBranch protection on main with required CI
Frequently Asked

Honest answers to the questions teams actually ask.

Continue Your Vibe Coding Path

You finished one masterclass. The Academy has more.

The DDS Vibe Academy ships new masterclasses on AI-first development, headless commerce, and synthetic-employee architectures. Return to the hub for the full library.