カスタムテンプレートと Custom code ブロックの違い
- カスタムテンプレートは、既存ブロックのレンダリングを独自のマークアップで置き換え、そのブロック自身のデータ(Header のタイトルとアイテム数、Summary の合計金額など)を渡してくれます。新しいものを追加するわけではなく、1 つのブロックの見た目を作り直すものです。
- Custom code ブロックは、任意の HTML または React の新しいブロックをカート内のどこにでも追加します。
カスタムテンプレートの使い方
- エディタでブロックを選択し、Code タブを開きます。
- デフォルトのテンプレートを編集します。カスタムテンプレートは JSX のみです(HTML か JSX かの選択肢があるのは Custom code ブロックだけです)。
- Compile をクリックします。コンパイルは型を取り除き JSX をトランスパイルするため、構文エラーを検出します。型エラーはコンパイルを止めません。エディタが入力中にインラインで指摘し、ブロックの props を自動補完するのと同じ IntelliSense が使われます。
- テンプレートをオンにすると、カートは組み込みのレンダリングの代わりにそれを使用します。
- Reset to default で、いつでもブロックの元のテンプレートを復元できます。
AI でテンプレートを書く
- コンパイルのルール(単一の式、
export defaultなし、import なし) - エディタの IntelliSense に表示されるものと一致する、ブロックが受け取る正確な props
- エディタが強制するロックされた関数シグネチャ
- ブロック固有のルール(金額のフォーマット、接続すべきハンドラ、アクセシビリティ要件)
- 現在のテンプレートを貼り付け、望む変更を記述する記入セクション
プロンプトはブロックごとに固有です。Copy AI prompt ボタンは、カスタムテンプレートをサポートするブロックにのみ表示されます。
テンプレートが置き換えるもの
保持されるもの: カート内でのブロックの位置、表示切り替え、設定(受け取る props には引き続き反映されます)、カートの Custom CSS パネル、そして組み込みのローディングスケルトンです。
最後の項目は意外に思われがちです。ブロックはテンプレートに到達する前にカートがまだ読み込み中かどうかを確認するため、読み込み中は組み込みのスケルトンがレンダリングされ、テンプレートはカートの準備が整ってから実行されます。ローディング状態を自作する必要はありません。
テンプレート内で利用できるもの
- 使えるフックは 5 つです:
useState、useEffect、useMemo、useRef、useCallback。加えて<>…</>のためのFragment。 - import はできません。 何も
importできず、スコープ内にReactオブジェクトも存在しないため、React.useReducerもReact.Childrenも使えません。上のリストにないフックは利用できません。 - props は読み取り専用です。 props を書き換えても意味のある結果にはなりません。カートを変更するには、props に直接書き込むのではなく、ブロックが提供するハンドラの props(
onClose、increment、selectPlanなど)を使ってください。 windowにはアクセスできます。 そのため、ブロックの props でカバーされないものが必要な場合、テンプレートはwindow.aftersell.cart経由で Cart SDK を呼び出せます。
すべてのブロックに共通する規約
*Htmlという props はサニタイズ済みのリッチテキストです。dangerouslySetInnerHTMLでレンダリングしてください。すでにカートのサニタイザーを通過しており、{{total_price}}のようなマーチャントトークンも解決済みです。stringで渡される価格は、ショップの金額フォーマットですでに整形済みです。numberの価格はセント単位です。ブロックはどちらか一方を渡し、各ブロックの表にどちらかが記載されています。- テンプレート内では
isLoadingは常にfalseです。 ブロックは組み込みのスケルトンをレンダリングし、カートの読み込みが完了してからテンプレートを呼び出すため、この prop は分岐のためではなく網羅性のために渡されています。
一部のブロックは特定の状態では何もレンダリングしないため、テンプレートが空のデータで呼び出されることはありません。Rewards のテンプレートが空の
milestones を受け取ることはなく、Subscription upgrade のテンプレートが null の view を受け取ることもありません。各ブロックのリファレンスに該当箇所が記載されているため、空状態の分岐は省略できます。カスタムテンプレートのスタイリング
2 つのクラスファミリー
つまり、
cart-internal-header__title はタイトルを組み込みのタイトルらしく見せているものであり、cart-external-header__title は見た目を変えたいときにつかむべきハンドルです。
小さな変更: 両方のクラス名を残す
cart-external-* フックを対象にした Custom CSS でスタイルを変更できます。
構造の変更: 両方のクラス名を外す
cart-internal-* を外すのは、組み込みの CSS が組み込みの DOM のために書かれているからです。 構造を変えたマークアップにこれらのクラスを残すと、もう存在しない要素を前提としたレイアウトルールを継承してしまいます。異なる子要素を期待する flex コンテナ、移動した要素間の余白、削除したものを基準にした位置指定などです。これは通常、組み込みのルールが勝ってしまうことで、自分の CSS が「効かない」という形で表面化します。
作ったものにスタイルを当てる方法は 2 つあります:
オプション 1: 独自のクラス名 + Custom CSS
.header や .title のようなクラスは、カート自身のクラス、他のアプリのテンプレート、将来のブロックと衝突するおそれがあります。
オプション 2: インラインスタイル
:hover などの疑似クラスは使えず、メディアクエリも使えず、ブロック間での再利用もできません。これらのいずれかが必要になったら、オプション 1 に切り替えてください。
アプローチの選び方
カートは shadow root 内でレンダリングされるため、テーマのスタイルシートはその内部に届きません。カスタムテンプレートのスタイルは、テーマからではなく、カート自身の Custom CSS パネルまたはインラインスタイルから当てる必要があります。Custom CSS を参照してください。
テンプレートが失敗したとき
ブロックは目に見えるエラーを出さずに静かに消えるため、公開前に必ずプレビューでテンプレートを確認してください。ブロックが消えている場合は、まずブラウザのコンソールを開きます。
そうでないと想定したテンプレートはどちらもクラッシュするため、次の 2 点には注意が必要です:
- null になり得る props。 多くの props は通常の状況で
nullになります(ロゴがない場合のlogoUrl、画像がない場合のimageUrl、単一バリアント商品のvariantTitleなど)。使う前に確認してください。 - 空になり得る配列。
discountTagsとdiscountCodesは、むしろ[]であることの方が多いです。
制限事項
- カスタムテンプレートは表示の上書きです。 カートに対してロジックを実行する(イベントを購読する、アイテムを追加する、変更に反応する)には、カスタムスクリプトと Cart SDK を使ってください。
- ほぼすべてのブロックがカスタムテンプレートをサポートしています。 例外は、Shopify 自身の決済ボタンをホストする Express payments ブロックと、Cart items コンテナ自体です。ただし、その中の Product 行はカスタムテンプレートをサポートしています。
- テンプレートはブロックの本質的な動作を変えることはできません。 変わるのはブロックのデータの見せ方であって、その背後にあるデータや動作ではありません。
各ブロックの props
Custom code ブロックは、ブロックのレンダリングを置き換えるのではなくマークアップを追加する唯一の場所なので、props も異なります: カート全体と、カートへの追加アクションです。Custom code ブロック → Props を参照してください。