ヘッドレスストアフロントは「簡単に配布できる」ようになったが「ポータブル」ではなかった

Hydrogenはヘッドレスストアフロントの構築を確かに簡単にしました。問題は、それが特定のランタイムに縛られてしまう点でした。Vercel Ship 26での発表によれば、VercelとShopifyはHydrogenを**ゼロから再構築(rebuild from the ground up)**する共同作業に着手します。キーワードは3つです。

  • オープンソース: コードは公開されます。
  • ランタイム非依存(Runtime Agnostic): JavaScriptが動く場所ならどこでも実行できます。
  • フレームワーク自由: Svelte、Nuxt、Next.jsはもちろん、カスタムフレームワークも可能です。

戦略はCore / Client / Serverの3層に分かれます。各層がどの「繰り返し作業」を排除するのかが本稿の核心です。

根拠資料: Vercel公式ブログ — Vercel and Shopify are rebuilding Hydrogen

日本のEC開発現場では、この変化は特に歓迎すべきものです。これまでShopify Headlessを導入するには「ランタイム依存」のリスクを甘受する必要がありましたが、Next.jsベースの自社インフラに載せる選択肢が現実的になりました。

Developer reviewing Hydrogen open source storefront architecture diagram on laptop screen System Abstract Visual

Core層 — 「誰もが毎回書き直していたあのコード」を一箇所に集約する

最も明確な例は金額フォーマットです。ウェブ標準だけで済ませるならこうなります。

// 標準のIntl APIでUSDをフォーマット
const price = new Intl.NumberFormat("en-US", {
  style: "currency",
  currency: "USD",
}).format(19.99); // "$19.99"

しかしShopify APIは純粋なnumberを返しません。MoneyV2というカスタム型で応答し、amountは文字列としてシリアライズされた符号付き10進数です。そのため、誰もがこうしたグルーコードを自前で書いてきました。

import { formatMoney, type MoneyV2 } from "@shopify/hydrogen";

// Core層が提供するフォーマッタ — グルーコードの保守から解放
export function formatPrice(money: MoneyV2, locale = "en-US") {
  return formatMoney(money, { locale }).toString(); // $19.99
}

結果は同じですが、もうグルーコードを書いたり保守したりする必要はありません。API仕様が変わっても、アップグレード一発で済みます。Coreを中央集約すれば、バグは一度直せばよく、改善は全ユーザーに自動配布されます。

Client層 — カート状態管理はimport一行に

カート状態は、コマースアプリを作ったことがある人なら誰でも一度は自前で書いたはずです。

// 従来の方式: 毎回異なる、毎回繰り返し
import { createContext, useContext, useState, useCallback } from "react";

const CartContext = createContext(null);

export function CartProvider({ children }) {
  const [cart, setCart] = useState(null);
  const addLine = useCallback(async (variantId, quantity) => {
    // 誰もが自前で書いてきたカスタムコード
  }, []);
  // applyDiscount, note, currency, error state, cross-tab sync, refetch on focus...
  return ({children});
}

export const useCart = () => useContext(CartContext);

問題は毎回違うコード、しかし目指すものは同じという点です。HydrogenのClient層はこれを単一のimportで処理します。

// Hydrogen方式: カート状態管理がimport一行
import { createCartComponents } from "@shopify/hydrogen/react";

const { useCartForm } = createCartComponents();

function AddToCartButton({ variantId }) {
  const { formProps, register } = useCartForm();
  return (
    <form {...formProps}>
      <input type="hidden" {...register("variantId")} value={variantId} />
      <button type="submit">Add to cart</button>
    </form>
  );
}

カート状態管理をベストプラクティスごと無償で手に入れられるわけです。現時点ではReact向けにHydrogen previewブランチで利用可能で、他のフレームワークも順次対応予定です。

Cloud runtime agnostic deployment diagram for Shopify Hydrogen headless commerce stack Developer Related Image

Server層 — フルスタックフレームワークの上にShopify APIを載せる

ストアフロントがパフォーマンスを犠牲にせずスケールするには、フルスタックのアプローチが必要です。静的コンテンツはCDNから即座に、在庫のような動的データはストリーミングで、という具合に。

オープンソースコミュニティは既にこれをNext.js、Nuxt、SvelteKitで解決しています。独自ランタイムに縛られないフルスタックの能力です。

// Next.js + Hydrogen: 型安全なクライアント + オンデマンド再検証
import { PRODUCT_QUERY } from "@/lib/gql";
import { storefrontConfig } from "@/lib/config";
import { cacheTag } from "next/cache";
import { createStorefrontClient } from "@shopify/hydrogen";

// オンデマンドで再検証可能な商品データのキャッシュ関数
export async function getProductData({ handle }) {
  "use cache";
  cacheTag(handle);

  const client = createStorefrontClient({
    type: "private_shared_rate_limit",
    config: storefrontConfig,
  });

  const { data } = await client.graphql(PRODUCT_QUERY, {
    variables: { handle },
  });

  return data;
}

Shopifyは既にHeadless sales channelでこれらのフレームワークをサポートしていますが、これまでは誰もが同じAPIコントラクトに対するバインディングを自前で書いてきました。この層での解決策は「より多くのコード」ではなくガイダンスです。人間もエージェントも、フレームワークが既にやっていることを再発明せず、そのまま使うべきだからです。このガイダンスはドキュメント・テンプレート・スキルとして配布されます。

⚠️ 実務上の注意: Cache Components互換性の落とし穴

Next.js 16のcacheComponents: true環境でi18nを組み込む際、以下の落とし穴を知らないと全く無関係に見えるビルドエラーが大量発生します。

落とし穴症状対応
app/layout.tsxがapp/[locale]/の上に存在rootParams.locale()がundefinedを返すルートレイアウトをapp/[locale]/layout.tsxへ移動し、元ファイルを削除
setRequestLocaleの使用キャッシュ無効化、強制動的レンダリングnext/root-paramsベースのgetLocale()へ置換
next-intlのLinkへ全面置換unstable_samples未定義エラーnext/linkを維持し、middlewareでプレフィックスリダイレクト
instantサンプルにlocale欠落ルートパラメータ未定義でビルド失敗全サンプルのparamsにlocaleを追加
レイアウトでheaders()を読むヘッダー未定義でビルド失敗サンプルにheaders: [["x-vercel-ip-postal-code", null]]を追加

特に最後の項目は、配送地域バナーのようなコンポーネントを1つ追加するだけでアプリ全体のinstantサンプルを修正する必要があるため、レイアウトレベルのサーバーコンポーネント追加は慎重に判断してください。

vercel.shopとの関係

Hydrogen以前にも、Vercelは自社テンプレートvercel.shopを運用していました。Next.js + Shopifyの組み合わせを簡単にするものでした。今回の再構築は、その経験をClient/Server層に吸収させる作業です。Hydrogenが安定化すれば、vercel.shopがこれを採用し、Next.js + Hydrogenのリファレンスとして位置づけられます。

Vercel and Shopify engineers collaborating on open source headless storefront codebase Technical Structure Concept

まとめ — 「ランタイムロックイン」を取り除くということ

今回の再構築の本質は、機能追加ではなく依存性の除去です。Coreは繰り返しコードを一箇所に集め、Clientはカートのような定型的な状態管理を吸収し、Serverはフレームワークのフルスタック能力をそのまま使います。

併せて読みたい記事

次のステップ学習の方向性

  1. Next.js 16 Cache Componentsのドキュメントを先に一通り確認してください。use cache、cacheTag、instantサンプルの概念を知らないと、上記の落とし穴が魔法のように見えてしまいます。
  2. next-intl + App Routerの組み合わせを小規模プロジェクトで実際に組んでみてください。rootParamsパターンは今後標準になる可能性が高いです。
  3. Hydrogen previewブランチをforkして、React向けClient層を先に体験してみてください。カスタムフレームワーク対応が来る前に感覚を掴んでおくと有利です。

日本のEC開発チームであれば、今すぐ本番導入するよりもサイドプロジェクトレベルでHydrogen + Next.jsの組み合わせを検証しておくことをお勧めします。ランタイム依存が消えるということは、すなわち「自社インフラに合わせて調整する余地」が生まれるということですから。

本コンテンツは、信頼性の高い情報源をもとにAIツールを活用して作成され、編集者によるレビューを経て公開されています。専門家によるアドバイスの代替となるものではありません。