mcpField
H
headless-hydrogen

headless-hydrogen

github.com/dragnoir開発5050 回インストール38 週間前に更新
リポジトリ

headless-hydrogen について

HydrogenまたはStorefront APIを使ったShopifyのヘッドレスストア構築を支援する開発スキルです。ストア機能やパフォーマンスの手法、Oxygenへのデプロイを案内します。

主な機能

  1. 01Hydrogenストアのセットアップ
  2. 02Storefront APIのデータクエリ
  3. 03カートと顧客アカウントの実装例
  4. 04検索とローカライズの例
  5. 05Oxygenへのデプロイ案内

活用シーン

  1. 01カスタムShopifyストアの構築
  2. 02カートと顧客ログイン機能の追加
  3. 03HydrogenストアのOxygenへのデプロイ

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/dragnoir/shopify-agent-skills --skill headless-hydrogen

インストール後、Claude Code で/headless-hydrogenを入力して読み込まれたか確認します。

詳細

Hydrogen によるヘッドレスコマース

このスキルを使うタイミング

次のような場合に、このスキルを使ってください。

  • カスタムのヘッドレスストアフロントを構築する
  • e コマースに Hydrogen フレームワークを使用する
  • Oxygen(Shopify のエッジホスティング)にデプロイする
  • Storefront API を使用する
  • 高性能なカスタムストアフロントを作成する
  • Shopify をカスタム技術スタックと連携する

Hydrogen とは?

Hydrogen は、次の技術を基盤とする Shopify 公式のヘッドレスコマースフレームワークです。

  • React Router - ルーティングとデータ読み込み用
  • React - コンポーネントベースの UI
  • GraphQL - Storefront API からのデータ取得
  • Oxygen - グローバルなエッジデプロイ(無料ホスティング)
主なメリット
  • 構築にすぐ使えるコンポーネント - 事前構築済みのコマースコンポーネント
  • 無料ホスティング - 追加料金なしで Oxygen にデプロイできます
  • デフォルトで高速 - SSR、プログレッシブエンハンスメント、ネストされたルート
  • Shopify ネイティブ - Shopify API との深い連携

はじめに

1. Hydrogen アプリを作成する
# Create new Hydrogen project
npm create @shopify/hydrogen@latest

# Follow the prompts:
# - Choose a template (Demo store, Hello World, Skeleton)
# - Enter your store URL
# - Select JavaScript or TypeScript
2. プロジェクト構造
hydrogen-app/
├── app/
│   ├── components/        # React components
│   ├── routes/            # Page routes
│   │   ├── _index.tsx     # Home page
│   │   ├── products.$handle.tsx  # Product page
│   │   └── collections.$handle.tsx
│   ├── styles/            # CSS files
│   ├── entry.client.tsx   # Client entry
│   └── entry.server.tsx   # Server entry
├── public/                # Static assets
├── .env                   # Environment variables
├── hydrogen.config.ts     # Hydrogen config
└── package.json
3. 環境を設定する
# .env
SESSION_SECRET=your-session-secret
PUBLIC_STOREFRONT_API_TOKEN=your-storefront-api-token
PUBLIC_STORE_DOMAIN=your-store.myshopify.com
4. 開発を開始する
npm run dev

基本概念

ルートとデータ読み込み
// app/routes/products.$handle.tsx
import { useLoaderData, type LoaderFunctionArgs } from "@remix-run/react";

export async function loader({ params, context }: LoaderFunctionArgs) {
  const { storefront } = context;
  const { handle } = params;

  const { product } = await storefront.query(PRODUCT_QUERY, {
    variables: { handle },
  });

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

  return { product };
}

export default function ProductPage() {
  const { product } = useLoaderData<typeof loader>();

  return (
    <div className="product-page">
      <h1>{product.title}</h1>
      <p>{product.description}</p>
      <ProductPrice data={product} />
      <AddToCartButton variantId={product.variants.nodes[0].id} />
    </div>
  );
}

const PRODUCT_QUERY = `#graphql
  query Product($handle: String!) {
    product(handle: $handle) {
      id
      title
      description
      handle
      variants(first: 1) {
        nodes {
          id
          price {
            amount
            currencyCode
          }
        }
      }
      featuredImage {
        url
        altText
      }
    }
  }
`;
Hydrogen コンポーネント
import {
  Image,
  Money,
  CartForm,
  CartLineQuantity,
  useCart,
} from '@shopify/hydrogen';

// Image Component
<Image
  data={product.featuredImage}
  aspectRatio="1/1"
  sizes="(min-width: 45em) 50vw, 100vw"
/>

// Money Component
<Money data={product.price} />

// Add to Cart
<CartForm
  route="/cart"
  action={CartForm.ACTIONS.LinesAdd}
  inputs={{
    lines: [{ merchandiseId: variantId, quantity: 1 }],
  }}
>
  <button type="submit">Add to Cart</button>
</CartForm>
カート管理
// app/routes/cart.tsx
import { CartForm } from "@shopify/hydrogen";
import { type ActionFunctionArgs } from "@remix-run/cloudflare";

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

  switch (action) {
    case CartForm.ACTIONS.LinesAdd:
      return await cart.addLines(inputs.lines);
    case CartForm.ACTIONS.LinesUpdate:
      return await cart.updateLines(inputs.lines);
    case CartForm.ACTIONS.LinesRemove:
      return await cart.removeLines(inputs.lineIds);
    default:
      throw new Error("Unknown cart action");
  }
}

export default function CartPage() {
  const cart = useLoaderData<typeof loader>();

  return (
    <div className="cart">
      {cart.lines.nodes.map((line) => (
        <CartLineItem key={line.id} line={line} />
      ))}
      <Money data={cart.cost.totalAmount} />
    </div>
  );
}
コレクション
// app/routes/collections.$handle.tsx
export async function loader({ params, context }: LoaderFunctionArgs) {
  const { handle } = params;
  const { storefront } = context;

  const { collection } = await storefront.query(COLLECTION_QUERY, {
    variables: { handle, first: 24 },
  });

  return { collection };
}

const COLLECTION_QUERY = `#graphql
  query Collection($handle: String!, $first: Int!) {
    collection(handle: $handle) {
      id
      title
      description
      products(first: $first) {
        nodes {
          id
          title
          handle
          featuredImage {
            url
            altText
          }
          priceRange {
            minVariantPrice {
              amount
              currencyCode
            }
          }
        }
      }
    }
  }
`;

高度なパターン

検索の実装
// app/routes/search.tsx
export async function loader({ request, context }: LoaderFunctionArgs) {
  const url = new URL(request.url);
  const searchTerm = url.searchParams.get("q");

  if (!searchTerm) {
    return { results: null };
  }

  const { storefront } = context;
  const { products } = await storefront.query(SEARCH_QUERY, {
    variables: { query: searchTerm, first: 20 },
  });

  return { results: products };
}

const SEARCH_QUERY = `#graphql
  query Search($query: String!, $first: Int!) {
    products(query: $query, first: $first) {
      nodes {
        id
        title
        handle
        featuredImage {
          url
          altText
        }
      }
    }
  }
`;
顧客認証
// app/routes/account.login.tsx
export async function action({ request, context }: ActionFunctionArgs) {
  const { customerAccount } = context;
  const formData = await request.formData();
  const email = formData.get("email");
  const password = formData.get("password");

  const { customerAccessTokenCreate } = await customerAccount.mutate(
    LOGIN_MUTATION,
    { variables: { input: { email, password } } },
  );

  if (customerAccessTokenCreate.customerAccessToken) {
    // Store token in session
    return redirect("/account");
  }

  return { errors: customerAccessTokenCreate.customerUserErrors };
}
ローカライズ
// Multi-currency and language support
export async function loader({ request, context }: LoaderFunctionArgs) {
  const { storefront } = context;

  // Get localized data
  const { localization } = await storefront.query(LOCALIZATION_QUERY);

  // Query with localization context
  const { product } = await storefront.query(PRODUCT_QUERY, {
    variables: { handle, country: "CA", language: "FR" },
  });

  return { product, localization };
}

Oxygen へのデプロイ

CLI からデプロイする
# Link to Shopify store
npx shopify hydrogen link

# Deploy to Oxygen
npx shopify hydrogen deploy
環境変数

Shopify 管理画面で環境変数を設定します。

  1. 販売チャネル > Hydrogen に移動します
  2. ストアフロントを選択します
  3. 環境変数を追加します
プレビューデプロイ

Git にプッシュするたびに、テスト用のプレビュー URL が作成されます。

# Push to create preview
git push origin feature-branch

独自のスタックを使用する

Hydrogen を使わない場合は、任意のフレームワークで Storefront API を使用できます。

ヘッドレスチャネルをインストールする
# In your Shopify admin, install the Headless channel
# Create a storefront and get API credentials
Next.js で使用する
// lib/shopify.ts
const domain = process.env.SHOPIFY_STORE_DOMAIN;
const storefrontAccessToken = process.env.SHOPIFY_STOREFRONT_ACCESS_TOKEN;

export async function shopifyFetch({ query, variables }) {
  const endpoint = `https://${domain}/api/2025-01/graphql.json`;

  const response = await fetch(endpoint, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "X-Shopify-Storefront-Access-Token": storefrontAccessToken,
    },
    body: JSON.stringify({ query, variables }),
  });

  return response.json();
}
Storefront Web Components

<script
  type="module"
  src="https://cdn.shopify.com/storefront-web-components/v1/storefront.js"
></script>

<shopify-product-provider store-domain="your-store.myshopify.com">
  <shopify-product handle="product-handle">
    <shopify-product-title></shopify-product-title>
    <shopify-product-price></shopify-product-price>
    <shopify-add-to-cart></shopify-add-to-cart>
  </shopify-product>
</shopify-product-provider>

パフォーマンスのベストプラクティス

  1. サーバーサイドレンダリング - 初回ページ読み込みに SSR を使用します
  2. ストリーミング - React Suspense を使って段階的に読み込みます
  3. 画像の最適化 - Hydrogen の Image コンポーネントを使用します
  4. コード分割 - 重要度の低いコンポーネントを遅延読み込みします
  5. キャッシュヘッダー - 適切なキャッシュポリシーを設定します
  6. プリフェッチ - ホバー時にリンクをプリフェッチします
// Streaming example
import { Suspense } from "react";

function ProductPage() {
  return (
    <div>
      <ProductInfo />
      <Suspense fallback={<LoadingSkeleton />}>
        <ProductRecommendations />
      </Suspense>
    </div>
  );
}

CLI コマンドのリファレンス

コマンド説明
npm create @shopify/hydrogen新しいプロジェクトを作成
npm run dev開発サーバーを起動
npm run build本番環境用にビルド
npx shopify hydrogen linkストアにリンク
npx shopify hydrogen deployOxygen にデプロイ
npx shopify hydrogen preview本番ビルドをプレビュー

リソース

API の詳細については、api-graphql スキルを参照してください。

コメント