この記事の対象: Shopifyテーマを触っている制作者・エンジニア。Liquidの基本文法がわかる人
「トップにだけ出したいバナー」「商品ページだけに出したい配送案内」「セール対象タグが付いた商品にだけ出すバッジ」。この3つは、実装のやり方がそれぞれ違います。Online Store 2.0のテーマではJSONテンプレートでセクションを配置するため、ページごとの出し分けは基本的にテンプレートを分けるだけで済みます。一方、商品タグや在庫状況といった動的な条件はLiquidの条件分岐でしか書けません。この記事では、Shopify セクション 表示条件 テンプレート別の出し分けについて、どちらを使うかの判断基準と、そのままコピーして動くコードを載せます。
前提の確認

作業を始める前に、環境を揃えます。
| 項目 | 必要なもの |
|---|---|
| テーマ | Online Store 2.0対応テーマ(templates/ に .json ファイルがあるテーマ) |
| 権限 | ストア管理画面の「テーマ」編集権限。パートナーアカウントなら Themes の read/write |
| 編集方法 | 管理画面のコードエディタ、または Shopify CLI(shopify theme dev) |
| 前提知識 | Liquidの {% if %} {% for %} がわかること |
Online Store 2.0のテーマかどうかの確認方法は簡単です。管理画面 → オンラインストア → テーマ → 「…」 → コードを編集 で templates/ フォルダを開き、index.json product.json のように .json 拡張子のファイルが並んでいれば2.0テーマです。index.liquid product.liquid のように .liquid だけなら旧テーマで、この記事のテンプレート分割の話は使えません(Liquidの条件分岐だけは使えます)。
作業前にテーマを複製しておいてください。テーマ一覧の「…」→「複製」で1クリックです。公開中テーマを直接編集すると、保存した瞬間に本番へ反映されます。
JSONテンプレート(静的)
- ページ種別ごとに出す・出さない
- 特定の商品グループだけ違う構成
- 管理画面から並び替え・編集できる
- コードを1行も書かなくてよい
Liquid条件分岐(動的)
- 商品タグ・在庫・価格で切り替え
- 顧客のログイン状態・タグ
- ページ内の一部分だけ変える
- 管理画面からは制御できない
判断はこの一点で決まります。「同じページを開いた人によって内容が変わるか」がNoならJSONテンプレート、YesならLiquidの条件分岐です。
テンプレート別に出し分ける(JSONテンプレート)

JSONテンプレートの中身を読む
まず templates/index.json を開いてください。こんな構造になっています。
{
"sections": {
"hero_banner": {
"type": "image-banner",
"settings": {
"image_height": "large"
}
},
"featured_products": {
"type": "featured-collection",
"settings": {
"collection": "frontpage",
"products_to_show": 8
}
}
},
"order": [
"hero_banner",
"featured_products"
]
}
読み方は3つ覚えれば足ります。
sectionsのキー(hero_banner)は自分で決めるIDです。テーマエディタで追加するとimage_banner_ABC123のようなランダム文字列になりますが、手書きするなら意味のある名前で構いませんtypeはsections/フォルダにあるLiquidファイル名から.liquidを除いたもの。sections/image-banner.liquidなら"image-banner"orderに並んだ順に上から描画されます。sectionsに定義してもorderに入れなければ表示されません。ここが最初のつまずきポイントです
つまり、「トップにだけ出す」は index.json の order に入れるだけ。商品ページに出したくないなら product.json に書かなければいいだけです。条件分岐は要りません。
ページ種別ごとのテンプレートファイル
Shopifyが用意しているテンプレートの種類は決まっています。よく使うものを挙げます。
| ファイル | 対応するページ |
|---|---|
templates/index.json |
トップページ |
templates/product.json |
商品詳細ページ |
templates/collection.json |
コレクション(商品一覧) |
templates/list-collections.json |
コレクション一覧 |
templates/page.json |
固定ページ |
templates/blog.json |
ブログ記事一覧 |
templates/article.json |
ブログ記事詳細 |
templates/cart.json |
カート |
templates/search.json |
検索結果 |
templates/404.json |
404ページ |
templates/customers/ 配下にはアカウント関連(login register account order addresses)が入ります。こちらはテーマによってLiquidテンプレートのままの場合があるので、開いて確認してください。
代替テンプレートで「一部の商品だけ」を変える
「予約商品だけ、商品ページに注意書きセクションを出したい」。この手の要求はタグ判定よりも代替テンプレートのほうが確実です。管理画面から商品ごとにテンプレートを選べるので、運用側で切り替えられます。
templates/product.preorder.json というファイルを新規作成します。ファイル名の規則は product.<好きな名前>.json です。名前部分は英小文字とハイフンのみにしてください。
{
"sections": {
"main": {
"type": "main-product",
"blocks": {
"title": { "type": "title", "settings": {} },
"price": { "type": "price", "settings": {} },
"buy_buttons": {
"type": "buy_buttons",
"settings": {
"show_dynamic_checkout": false
}
}
},
"block_order": ["title", "price", "buy_buttons"]
},
"preorder_notice": {
"type": "rich-text",
"settings": {
"color_scheme": "scheme-2"
}
}
},
"order": ["preorder_notice", "main"]
}
書き換える箇所は3つです。"type": "rich-text" は自分のテーマにあるセクション名に合わせてください(テーマによっては custom-text などの名前です)。"scheme-2" はテーマのカラースキーム名で、これもテーマ依存です。show_dynamic_checkout は予約商品でShop Payの即時購入ボタンを消したいときの設定で、不要なら行ごと削除して構いません。
保存したら、管理画面の 商品 → 該当商品 → 右カラム下部「テーマテンプレート」で preorder を選びます。ここに出てこないときは、ファイル名が product. で始まっているか確認してください。preorder-product.json のような名前だと候補に出ません。
コレクションとページも同じ規則です。
templates/collection.sale.json → コレクション編集画面で選択
templates/page.contact.json → 固定ページ編集画面で選択
templates/product.gift.json → 商品編集画面で選択
この流れで作れば、以降の割り当てはコードを触らずに運用担当ができます。制作者が引き渡した後に手が離れる作りになるのが、テンプレート分割の一番の利点です。
テーマエディタから代替テンプレートを作る
コードを書かずに済ませたいなら、テーマエディタ側でも作れます。テーマエディタ上部のページ選択プルダウン → 商品 → 「テンプレートを作成」。ベースにするテンプレートを選ぶとコピーが作られます。生成されるファイル名は product.<入力した名前>.json で、コードエディタからも見えます。
デザイナーに渡すときはこちらの手順を伝えるほうが早いです。ただし生成されるセクションIDがランダム文字列になるので、後からコードで管理したい場合は手書きのほうが読みやすくなります。
Liquidの条件分岐で出し分ける
テンプレートでは対応できない「同じページでも内容が変わる」ケースをLiquidで書きます。
テンプレート名で判定する
セクション内部で「今どのページか」を知りたいときは request.page_type を使います。ヘッダーやフッターのように全ページ共通のセクションで使う書き方です。
{% comment %} sections/announcement-bar.liquid など全ページ共通セクション内 {% endcomment %}
{% if request.page_type == 'index' %}
<div class="ts-topbanner">
<p>トップページ限定のお知らせをここに書きます</p>
</div>
{% endif %}
request.page_type が返す値は次のとおりです。
| 値 | ページ |
|---|---|
index |
トップ |
product |
商品詳細 |
collection |
コレクション |
list-collections |
コレクション一覧 |
page |
固定ページ |
blog |
ブログ一覧 |
article |
ブログ記事 |
cart |
カート |
search |
検索結果 |
customers/login |
ログイン |
404 |
404 |
複数ページで出したいときは or でつなぎます。
{% if request.page_type == 'product' or request.page_type == 'collection' %}
<p class="ts-shipping-note">5,000円以上のご購入で送料無料です</p>
{% endif %}
代替テンプレートを判定したいときは template.suffix です。product.preorder.json が使われているページでは template.suffix が preorder になります。
{% if template.suffix == 'preorder' %}
<p class="ts-preorder-lead">この商品は予約受付中です。発送は入荷後になります。</p>
{% endif %}
template をそのまま出力すると product.preorder のような文字列になるので、contains でまとめて判定することもできます。
{% if template contains 'preorder' %}
<!-- product.preorder でも page.preorder でも通る -->
{% endif %}
変更箇所は 'preorder' の部分だけ。自分が作ったサフィックス名に置き換えてください。
商品タグで出し分ける
ここからがテンプレートでは書けない領域です。商品タグでの分岐は product.tags に対して contains を使います。
{% comment %} sections/main-product.liquid の商品情報ブロック内 {% endcomment %}
{% if product.tags contains 'ギフト対応' %}
<div class="ts-gift-note">
<p>ラッピング袋とメッセージカードをお付けできます。カート画面でご指定ください。</p>
</div>
{% endif %}
'ギフト対応' を自分のタグ名に置き換えます。タグは大文字小文字を区別します。Gift と gift は別物として扱われるので、運用担当と表記を決めておいてください。日本語タグも問題なく使えます。
複数タグのいずれかで出したいときは、配列を作ってループする形が読みやすくなります。
{% assign target_tags = 'ギフト対応,のし対応,ラッピング可' | split: ',' %}
{% assign has_target = false %}
{% for tag in target_tags %}
{% if product.tags contains tag %}
{% assign has_target = true %}
{% break %}
{% endif %}
{% endfor %}
{% if has_target %}
<div class="ts-gift-note">
<p>ギフト包装に対応している商品です。</p>
</div>
{% endif %}
'ギフト対応,のし対応,ラッピング可' をカンマ区切りで書き換えるだけで対象タグを増やせます。{% break %} を入れているのは、1つ見つかった時点でループを抜けて無駄な走査を止めるためです。
コレクションページで商品カードごとに出し分けるなら、for ループの中で同じ判定をします。
{% for product in collection.products %}
<div class="ts-card">
{% if product.tags contains 'NEW' %}
<span class="ts-card__badge ts-card__badge--new">NEW</span>
{% endif %}
<a href="{{ product.url }}">
{{ product.featured_image | image_url: width: 600 | image_tag: loading: 'lazy', alt: product.title }}
<p class="ts-card__title">{{ product.title }}</p>
<p class="ts-card__price">{{ product.price | money }}</p>
</a>
</div>
{% endfor %}
ts- プレフィックスのクラス名は、テーマ本体のCSSと衝突させないための命名です。自分のプロジェクトの規約に合わせて変更してください。image_url: width: 600 の数値は表示サイズに合わせます。
在庫・価格・販売状態で出し分ける
タグ以外でよく使う判定を並べます。
{% comment %} 売り切れ {% endcomment %}
{% unless product.available %}
<p class="ts-soldout">現在品切れです</p>
{% endunless %}
{% comment %} セール中(元値が設定されていて、それより安い) {% endcomment %}
{% if product.compare_at_price > product.price %}
{% assign off = product.compare_at_price | minus: product.price | times: 100.0 | divided_by: product.compare_at_price | round %}
<span class="ts-sale-badge">{{ off }}%OFF</span>
{% endif %}
{% comment %} 在庫が少ない(バリエーションが1つの商品向け) {% endcomment %}
{% if product.variants.size == 1 and product.selected_or_first_available_variant.inventory_management != blank %}
{% assign qty = product.selected_or_first_available_variant.inventory_quantity %}
{% if qty > 0 and qty <= 5 %}
<p class="ts-lowstock">残り{{ qty }}点です</p>
{% endif %}
{% endif %}
割引率の計算で times: 100.0 と小数にしているのは、Liquidが整数同士の除算で小数点以下を切り捨てるためです。times: 100 と書くと計算結果がずれます。qty <= 5 の 5 はしきい値なので、扱う商材に合わせて変えてください。
在庫数の表示については注意が必要です。inventory_quantity は在庫追跡が有効な商品でのみ意味のある値を返します。追跡していない商品では 0 が返るため、上の例では inventory_management が空でないことを先に確認しています。バリエーションが複数ある商品では、選択中バリエーションの在庫をJavaScriptで切り替える処理が別途必要になります。
なお、セール中かどうかを商品カード側で判定するときは product.compare_at_price ではなく product.compare_at_price_max を見るほうが安全です。バリエーションごとに元値が違う商品では、代表バリエーションの値だけを見ていると「一部だけセール」の商品を取りこぼします。
{% if product.compare_at_price_max > product.price_min %}
<span class="ts-sale-badge">SALE</span>
{% endif %}
顧客の状態で出し分ける
会員向け価格や卸売の案内は顧客オブジェクトで判定します。
{% if customer %}
<p class="ts-member">{{ customer.first_name }}様、いつもありがとうございます</p>
{% if customer.tags contains 'wholesale' %}
<p class="ts-wholesale">卸価格が適用されています</p>
{% endif %}
{% else %}
<p class="ts-guest"><a href="{{ routes.account_login_url }}">ログイン</a>すると会員価格が表示されます</p>
{% endif %}
'wholesale' を自分の顧客タグに置き換えます。リンク先は /account/login と直書きせず routes.account_login_url を使ってください。多言語ストアでURLプレフィックスが付いたときに、こちらは自動で追従します。
セクションを Section Rendering API で後から取得する構成にしている場合は、その取得リクエストにログイン状態が乗るかを確認してください。顧客ごとの出し分けを含むセクションを非同期で差し替えると、ログイン前の内容が残ることがあります。
テーマ設定で管理画面から制御する
「出す・出さない」を運用側で切り替えたい要求には、セクションスキーマにチェックボックスを足すのが最短です。コードを触れない担当者でも操作できます。
sections/ に新規ファイルを作ります。ここでは sections/ts-notice-bar.liquid とします。
{% if section.settings.enabled %}
<div class="ts-notice-bar" style="background: {{ section.settings.bg_color }}; color: {{ section.settings.text_color }};">
<div class="ts-notice-bar__inner">
{% if section.settings.text != blank %}
<p class="ts-notice-bar__text">{{ section.settings.text }}</p>
{% endif %}
{% if section.settings.link_url != blank and section.settings.link_label != blank %}
<a class="ts-notice-bar__link" href="{{ section.settings.link_url }}">{{ section.settings.link_label }}</a>
{% endif %}
</div>
</div>
{% endif %}
<style>
.ts-notice-bar {
padding: 10px 16px;
font-size: 14px;
line-height: 1.6;
}
.ts-notice-bar__inner {
max-width: 1200px;
margin-inline: auto;
display: flex;
flex-wrap: wrap;
gap: 8px 16px;
align-items: center;
justify-content: center;
text-align: center;
}
.ts-notice-bar__text {
margin: 0;
}
.ts-notice-bar__link {
color: inherit;
text-decoration: underline;
text-underline-offset: 2px;
}
</style>
{% schema %}
{
"name": "お知らせバー",
"settings": [
{
"type": "checkbox",
"id": "enabled",
"label": "表示する",
"default": true
},
{
"type": "text",
"id": "text",
"label": "お知らせ文",
"default": "年末年始の発送スケジュールについて"
},
{
"type": "url",
"id": "link_url",
"label": "リンク先"
},
{
"type": "text",
"id": "link_label",
"label": "リンクの文言",
"default": "詳しく見る"
},
{
"type": "color",
"id": "bg_color",
"label": "背景色",
"default": "#1a1a1a"
},
{
"type": "color",
"id": "text_color",
"label": "文字色",
"default": "#ffffff"
}
],
"presets": [
{
"name": "お知らせバー"
}
]
}
{% endschema %}
変更する箇所は、ts-notice-bar のクラス名(自分の命名規則に合わせる)、max-width: 1200px(テーマのコンテナ幅に合わせる)、色のデフォルト値の3つです。それ以外はそのまま動きます。
presets を書いておくと、テーマエディタの「セクションを追加」の一覧に出てきます。これが無いとJSONに手書きしないと配置できないので、忘れずに入れてください。
配置は templates/index.json の sections と order に足すだけです。
{
"sections": {
"notice": {
"type": "ts-notice-bar",
"settings": {
"enabled": true,
"text": "年末年始の発送スケジュールについて",
"link_url": "/pages/shipping",
"link_label": "詳しく見る"
}
},
"hero_banner": {
"type": "image-banner",
"settings": {}
}
},
"order": ["notice", "hero_banner"]
}
CSSをセクションファイル内の <style> に書いているのは意図的です。外部CSSに追記すると、そのセクションを出さないページでも読み込まれます。セクション固有のスタイルはセクションと同じ場所に置いたほうが、後から消すときに漏れません。分量が増えて数百行になるようなら assets/ts-notice-bar.css に分けて、セクション冒頭で {{ 'ts-notice-bar.css' | asset_url | stylesheet_tag }} を書く形に切り替えます。
表示期間で自動的に切り替える
「セール告知バーを期間中だけ出す」ような要求は、担当者の手動ON/OFFに頼らず日付で判定できます。スキーマに text 型で開始日と終了日を持たせ、date フィルタで数値に直して比較します。
{% assign now = 'now' | date: '%s' | plus: 0 %}
{% assign start = section.settings.start_at | date: '%s' | plus: 0 %}
{% assign end = section.settings.end_at | date: '%s' | plus: 0 %}
{% if section.settings.start_at == blank or now >= start %}
{% if section.settings.end_at == blank or now <= end %}
<div class="ts-campaign-bar">
<p>{{ section.settings.text }}</p>
</div>
{% endif %}
{% endif %}
入力形式は 2026-12-24 00:00:00 のように書いてもらいます。日付が空のときは制限なしとして通す作りにしてあるので、片方だけ入れる運用もできます。注意点はタイムゾーンで、'now' はストアの管理画面で設定したタイムゾーンではなくUTCで評価されます。日本時間で切り替えたいなら、比較する側に9時間分(| minus: 32400)を足し引きするか、入力値をUTCで書いてもらってください。境目の時刻がずれる事故は、公開当日の朝に必ず気づかれます。
セクション単位でスクリプトを持たせる書き方については、スライダーの実装記事で素のJavaScriptだけで組む手順を扱っています。セクション固有のCSSとJSをどこに置くかという話は、この記事の判断と地続きです。
どちらを使うかの判断表
実際の要求ごとに、選ぶべき手段を整理します。
| やりたいこと | 手段 | 理由 |
|---|---|---|
| トップだけにバナー | index.json に配置 |
条件分岐が要らない |
| 商品ページ全部に配送案内 | product.json に配置 |
同上 |
| ヘッダー内にトップだけの一文 | request.page_type 判定 |
ヘッダーは全ページ共通セクション |
| 予約商品だけ構成を変える | product.preorder.json |
運用側で割り当てできる |
| ギフト対応タグの商品にバッジ | product.tags contains |
タグは商品ごとに動的に変わる |
| 売り切れ商品に注記 | product.available |
在庫はリアルタイムで変わる |
| セール中だけ割引率表示 | compare_at_price 比較 |
価格設定に連動させる |
| 会員だけに卸価格の案内 | customer.tags |
ログイン状態で変わる |
| 期間限定の告知 | 日付比較 | 担当者の消し忘れを防ぐ |
| 担当者が管理画面でON/OFF | スキーマに checkbox |
コードを触らせない |
判断が割れやすいのは「予約商品」のケースです。タグ判定でも書けますが、代替テンプレートにしておくとセクションの並び順ごと変えられるのが違いになります。「予約商品はレビューセクションを消して、注意書きを一番上に持ってくる」のような要求は、条件分岐で書くと main-product.liquid が分岐だらけになります。構成が変わるならテンプレート、要素が1つ増えるだけならタグ判定、と切り分けてください。
もうひとつ迷いやすいのが「セール期間中だけ全商品ページに帯を出す」です。テンプレートを product.sale.json として作ると、全商品に割り当て直す作業が発生し、終了後に戻す作業も同じだけ発生します。この場合は共通セクション側で日付判定を書くほうが軽く済みます。割り当て作業が商品数に比例するならテンプレートを選ばない、という基準を1つ持っておくと判断が速くなります。
Shopifyのカスタマイズは、こうしたテーマ側の工夫でできる範囲と、アプリや Shopify Functions が必要になる範囲の線引きがあります。何がテーマだけで完結して、何がそうでないかは別記事で整理しています。
メタフィールドで出し分ける

タグは商品一覧の絞り込みにも使われるため、表示制御専用に増やすと管理画面が散らかります。純粋に表示のためだけの情報なら、メタフィールドのほうが向いています。
管理画面 → 設定 → カスタムデータ → 商品 → 「定義を追加」で作ります。
| 項目 | 入力値の例 |
|---|---|
| 名前 | 発送目安 |
| ネームスペースとキー | custom.shipping_note |
| タイプ | 単一行のテキスト |
作ったら商品編集画面の下部に入力欄が出るので、値を入れます。Liquidから読むコードはこうなります。
{% assign note = product.metafields.custom.shipping_note %}
{% if note != blank %}
<div class="ts-shipping-note">
<p>{{ note | escape }}</p>
</div>
{% endif %}
custom.shipping_note を自分が作った定義に置き換えます。| escape を付けているのは、入力値にHTMLが混ざったときにそのまま出力されないようにするためです。リッチテキスト型のメタフィールドを使う場合は | metafield_tag を使ってください(この場合はHTMLとして出力されます)。
真偽値型のメタフィールドを使えば、商品ごとのON/OFFスイッチが作れます。
{% if product.metafields.custom.hide_reviews == true %}
{% comment %} レビューセクションを出さない {% endcomment %}
{% else %}
{% render 'ts-reviews', product: product %}
{% endif %}
真偽値型は == true で比較します。文字列の 'true' ではないので注意してください。ここを == 'true' と書くと常にfalse扱いになります。
参照型(商品・コレクション・ファイル)のメタフィールドも表示制御に使えます。「この商品ページに、関連する別商品を3件だけ出す」という要求は、商品参照のリスト型メタフィールドを作って商品側で選んでもらう形が確実です。自動レコメンドと違い、出る中身を運用担当が完全に決められます。
{% assign related = product.metafields.custom.related_products.value %}
{% if related != blank and related.size > 0 %}
<div class="ts-related">
{% for item in related %}
<a href="{{ item.url }}">{{ item.title }}</a>
{% endfor %}
</div>
{% endif %}
参照型は .value を付けて実体を取り出す点が単一行テキストと違います。付け忘れるとオブジェクトのIDらしき文字列が出るので、表示が崩れたらここを疑ってください。
メタフィールドの値をテーマエディタから参照する「動的ソース」機能も使えます。セクションスキーマの text や image_picker 型の設定には、テーマエディタ上でメタフィールドを紐づけるアイコンが出ます。これを使えばLiquidを書かずに商品ごとの値を差し込めるので、繰り返し使うセクションでは先に試す価値があります。
うまくいかないとき
セクションが表示されない
確認する順番はこれです。
orderに入っているか。sectionsに書いただけでは出ません。ここが一番多い原因ですtypeの綴りとファイル名が一致しているか。sections/ts-notice-bar.liquidに対して"type": "ts-notice-bar"。.liquidは書きません- JSONの構文エラー。末尾カンマ、シングルクォート、コメントはJSONでは使えません。コードエディタが赤く表示するので保存前に見てください
{% schema %}の中身が壊れていないか。スキーマJSONが不正だとセクション全体が読み込まれません。エディタ上でエラーメッセージが出ます
代替テンプレートが管理画面の選択肢に出ない
ファイル名の規則違反がほぼ全てです。product.<名前>.json の形式で、<名前> は英小文字・数字・ハイフンのみ。日本語やアンダースコアを使うと候補に出ません。作成後に管理画面をリロードしても出ない場合は、テーマが下書き状態でプレビュー中かどうかも確認してください。下書きテーマのテンプレートは、そのテーマをプレビューしている状態でのみ選択できます。
タグ判定が効かない
- 大文字小文字。
Giftとgiftは別です。判定側を{% assign tags_down = product.tags | join: ',' | downcase %}してtags_down contains 'gift'にすると吸収できます - 前後の空白。管理画面でタグを入力するときに空白が入っていると
containsで拾えないことがあります containsの部分一致。product.tags contains 'new'はnewarrivalというタグにもマッチします。完全一致で判定したいときは配列をループして{% if tag == 'new' %}と比較してください
request.page_type が期待した値にならない
代替テンプレートを使っていても request.page_type は product のままです。サフィックスまで見たいなら template.suffix を使ってください。この2つは別の情報です。また、パスワード保護ページや /challenge のようなシステムページでは想定外の値になることがあるので、else 側の挙動も確認しておきます。
テーマエディタでは出るのに公開後に出ない
テーマエディタはセクションを常に描画するモードで動くため、{% if %} で消しているセクションでもプレビューには枠が見えることがあります。逆に、テーマエディタで編集した内容が index.json に保存されるまでタイムラグがあるケースもあります。判断に迷ったら、テーマのプレビューURL(テーマ一覧の「…」→「プレビュー」)で確認してください。エディタ内プレビューとは別のレンダリングになります。
開発ストアでは出るのに本番で出ない
コードは同じでも、判定の材料が本番に無いケースがあります。よくあるのは3つ。メタフィールド定義を本番ストアに作っていない(定義はテーマではなくストアに属するので、テーマをアップロードしても付いてきません)。タグの表記が違う(開発では gift、本番では Gift で登録されている)。代替テンプレートを商品に割り当てていない(テンプレートファイルは移っても、どの商品に当てるかの情報は移りません)。テーマを移した直後に出し分けが効かなくなったら、コードより先にこの3つを見てください。
条件分岐が増えて読めなくなった
main-product.liquid に {% if %} が5個以上並び始めたら、テンプレート分割に切り替える合図です。分岐で書き続けると、後から「この条件はどのケース向けだったか」が誰にもわからなくなります。運用担当が管理画面から選べる形にしたほうが、引き渡した後の事故が減ります。
分岐を残したまま整理したいなら、判定と出力を分けるのが手です。セクション冒頭で {% assign is_preorder = ... %} のように真偽値を作っておき、本文側は {% if is_preorder %} だけを書く。条件の中身を1か所に集めておけば、仕様が変わったときに直す場所が1行で済みます。
WordPressで同じような「プラグインを増やさずにテーマ側で解決する」判断をした例は、ブログカードの実装記事にまとめています。考え方の型は共通です。
引き渡す前に決めておくこと
出し分けを実装した後、運用担当に渡す段階でつまずくポイントがあります。コードの問題ではなく、取り決めの問題です。
タグの命名規則を文書に残す。 ギフト対応 で判定しているのに、後から入った担当者が ギフト と登録すればバッジは出ません。使っているタグの一覧と、それが何を制御しているかを1枚の表にしてストア側に置いてもらいます。
代替テンプレートの割り当て一覧を作る。 どの商品にどのテンプレートが当たっているかは、管理画面から一覧で見る手段がありません。商品数が多いストアでは、Shopify の管理画面から商品CSVをエクスポートすると Template Suffix 列で確認できます。この方法だけは伝えておくと、後から棚卸しができます。
「出ないとき、どこを見るか」を3行で渡す。 タグの表記を確認する、テンプレートの割り当てを確認する、テーマが公開中のものか確認する。この3つを書いた紙が1枚あるだけで、問い合わせの多くは相手側で解決します。
出し分けは作った時点が完成ではなく、運用担当が触り続けられて初めて完成です。コードの美しさより、半年後に別の人が読んで直せるかで設計してください。
よくある質問
JSONテンプレートとLiquidテンプレートは併用できますか
できます。同じテーマ内で templates/index.json と templates/page.custom.liquid が共存しても問題ありません。ただし同名で product.json と product.liquid の両方を置くと、どちらが優先されるかがテーマの状態に依存するため避けてください。片方を削除するか、名前を分けて管理します。
セクションを非表示にするとページの表示速度は上がりますか
{% if %} で囲んで出力を止めた場合、そのHTMLとインラインCSSは生成されないので転送量は減ります。ただし外部CSSファイルに書いたスタイルや、テーマ共通で読み込んでいるJavaScriptは残ります。速度を狙うなら、セクション固有のCSSはセクションファイル内か個別ファイルに分け、必要なページでのみ読み込む構成にしてください。
商品タグでの出し分けは何個までなら大丈夫ですか
技術的な上限より、運用の限界が先に来ます。表示制御用のタグが増えるほど、商品登録時の付け忘れが起きやすくなります。タグを絞り込みフィルタにも使っているストアでは、表示制御専用のタグはメタフィールドに移すほうが安全です。メタフィールドなら定義した項目が商品編集画面に常に表示されるため、入力漏れに気づきやすくなります。
代替テンプレートは何個まで作れますか
実務では種類が増えるほど「どの商品にどれが当たっているか」の把握が難しくなります。種類が増えてきたら、テンプレートを分けるのではなくセクションのスキーマ設定で吸収できないか見直してください。テンプレート数が増えると、テーマ更新時に全テンプレートへ同じ修正を入れる手間も比例して増えます。
テーマをアップデートしたら出し分けの設定は消えますか
テーマの新バージョンを新規インストールした場合、テンプレートJSONもセクションファイルも新しいものに置き換わるため、カスタマイズは引き継がれません。追加したセクションファイルとテンプレートJSONは、アップデート前にダウンロード(テーマ一覧の「…」→「テーマファイルをダウンロード」)して手元に残し、新テーマへ移植する前提で作業してください。Shopify CLIでGit管理していれば差分で追えます。
出し分けの条件を管理画面から編集できるようにするには
セクションスキーマの text 型で対象タグ名を入力させ、Liquid側で section.settings.target_tag として受け取る形にします。{% if product.tags contains section.settings.target_tag %} と書けば、タグ名を変えたいときにコードを触らずに済みます。ただし入力ミスがそのまま「何も出ない」になるので、select 型で候補を固定できるならそちらが安全です。
まとめ
出し分けの実装は、次の順で検討すると迷いません。
- ページ種別だけで決まるか → JSONテンプレートに配置するだけ。条件分岐を書かない
- 商品や固定ページごとに構成が変わるか → 代替テンプレートを作り、運用側で割り当ててもらう
- 同じページでも見る人・状態で変わるか → Liquidの条件分岐。タグ・在庫・顧客で判定
- 担当者がON/OFFしたいか → セクションスキーマに
checkboxを足す
条件分岐で書けることを全部条件分岐で書くと、半年後に触れないテーマができあがります。テンプレートで済むものはテンプレートに寄せて、Liquidの分岐は「動的にしか判定できないもの」だけに絞る。この線引きが、引き渡した後も運用が回るテーマとそうでないテーマの分かれ目です。



