📘 HubSpot CMS 組み込み構築 教科書
Chapter 6

データ活用・動的コンテンツ

ブログ機能の深掘りとタグ設計戦略・完全ページネーション実装・HubDBによる動的データ管理・スマートコンテンツによるパーソナライズまで。HubSpotのデータ層を使いこなす章です。

🎯 対象レベル:中級〜上級
⏱ 読了目安:90〜120分
🔗 前章:第5章 テーマ設計・構築

この章の内容

  1. ブログ機能の設計思想(1ブログ vs 複数ブログ)
  2. タグ設計戦略:Starterプランでのカテゴリ代替
  3. ページネーション完全実装
  4. HubDB の設計と活用
  5. HubDB 動的ページ生成(Dynamic Pages)
  6. スマートコンテンツ(パーソナライズ)の実装
  7. ブログ高度活用パターン集
  8. 第6章まとめ
Section 6-1

ブログ機能の設計思想(1ブログ vs 複数ブログ)

HubSpot CMSのブログ設計における最初の判断は、 「1つのブログで複数コンテンツ種別を運用するか、複数ブログを使うか」です。 この選択はプランと運用体制の両方に依存します。

プラン別のブログ設計選択肢

プランブログ数推奨設計
Starter 1つのみ 1ブログ + タグで種別管理(後述のタグ設計戦略が必須)
Professional / Enterprise 最大100 コンテンツ種別ごとに別ブログを設置する構成が理想

複数ブログ構成のメリットと設計

Professional以上で複数ブログを使う場合、コンテンツ種別ごとに独立したURLとテンプレートを持てます。

ブログ名(管理画面)URL用途テンプレート
メインブログ/blogブログ記事・コラムblog-listing / blog-post
セミナー/seminarセミナー・イベント情報seminar-listing / seminar-post
お知らせ/newsプレスリリース・更新情報news-listing / news-post
事例/case導入事例・カスタマーストーリーcase-listing / case-post
✅ 複数ブログ設計の実装ポイント

複数ブログを使う場合、blog_recent_posts() の第1引数にブログIDまたはブログスラッグを指定します。
"default" はデフォルトブログのエイリアスです。
ブログIDはHubSpot管理画面の URL(app.hubspot.com/blog/PORTAL_ID/BLOG_ID/posts)で確認できます。

HubL — 複数ブログを扱う実装例
{# ブログIDを変数に定義して管理(保守性UP)#}
{% set blog_ids = {
  "main"    : "default",
  "seminar" : "12345678",   {# セミナーブログのID #}
  "news"    : "23456789",   {# お知らせブログのID #}
  "case"    : "34567890"    {# 事例ブログのID #}
} %}

{# 各ブログから最新記事を取得してトップページに表示 #}
{% set latest_seminars = blog_recent_posts(blog_ids.seminar, 3) %}
{% set latest_cases    = blog_recent_posts(blog_ids.case, 3) %}
{% set latest_news     = blog_recent_posts(blog_ids.news, 5) %}

{# セミナーの件数確認 #}
{% set seminar_total = blog_total_post_count(blog_ids.seminar) %}
<p>全{{ seminar_total }}件のセミナー情報</p>

{# 複数ブログの横断検索(最新記事をまとめて取得) #}
{% set all_recent = blog_recent_posts("default", 3) %}
{% set all_recent = all_recent + blog_recent_posts(blog_ids.seminar, 3) %}
{% set all_recent = all_recent + blog_recent_posts(blog_ids.news, 3) %}
⚠️ 複数ブログの横断ソートは HubL では不可

複数ブログから取得した記事を公開日順でソートして表示したい場合、 HubLの sort フィルターは使えますが、publish_date がUNIXタイムスタンプのため 直接ソートするには工夫が必要です。 完全な横断検索が必要な場合はHubSpot Search API(JavaScript)での実装を検討してください。


Section 6-2

タグ設計戦略:Starterプランでのカテゴリ代替

HubSpotにはWordPressの「カテゴリ」に相当する概念がなく、 タグがカテゴリの役割も兼ねます。 特にStarterプランで1ブログしか使えない場合、 タグ設計がサイト全体の情報設計を左右する最重要要素になります。

タグ設計の原則:命名規則でロールを分離する

🏷️ タグ命名規則の例(プレフィックスで役割を明示)

コンテンツ種別(type:プレフィックス)
type:blog type:seminar type:news type:case type:whitepaper
カテゴリ(cat:プレフィックス)
cat:marketing cat:sales cat:hubspot cat:beginner
ステータス・属性(status:プレフィックス)
status:featured status:pickup status:archive
通常タグ(キーワード、プレフィックスなし)
HubSpot MA BtoBマーケティング リード獲得

タグ判定ロジックの実装

HubL — タグプレフィックスで種別・属性を判定する
{# ======================================================
   タグから複数のロールを同時に判定するパターン
   記事詳細(blog-post.html)の冒頭で実行
====================================================== #}

{% set ns = namespace(
  content_type  = "blog",
  categories    = [],
  is_featured   = false,
  is_pickup     = false
) %}

{% for tag in content.tag_list %}

  {# ① コンテンツ種別の判定 #}
  {% if tag.slug starts_with "type:" %}
    {% set ns.content_type = tag.slug|replace("type:", "") %}

  {# ② カテゴリの収集(配列に追加) #}
  {% elif tag.slug starts_with "cat:" %}
    {% set ns.categories = ns.categories + [tag.slug|replace("cat:", "")] %}

  {# ③ ステータスフラグの判定 #}
  {% elif tag.slug == "status:featured" %}
    {% set ns.is_featured = true %}
  {% elif tag.slug == "status:pickup" %}
    {% set ns.is_pickup = true %}
  {% endif %}

{% endfor %}

{# 判定結果を活用 #}
<article class="post post--{{ ns.content_type }}
  {% if ns.is_featured %} post--featured{% endif %}
  {% if ns.is_pickup %} post--pickup{% endif %}">

  {% if ns.is_featured %}
    <span class="badge badge--featured">注目</span>
  {% endif %}

  {% if ns.categories %}
    <div class="post-categories">
      {% for cat in ns.categories %}
        <a href="{{ blog_tag_url("default", "cat:" ~ cat) }}"
           class="category-link">{{ cat }}</a>
      {% endfor %}
    </div>
  {% endif %}

</article>

タグナビゲーション:表示用タグの制御

プレフィックス付きタグ(type: / cat: / status:)は 記事の表示タグ一覧に出したくない場合が多いです。 フィルタリングするパターンを用意しておきます。

HubL — 表示用タグのみ抽出する
{# type: / cat: / status: を除いた「表示用タグ」のみ抽出 #}
{% set display_tags = [] %}
{% for tag in content.tag_list %}
  {% if not (
    tag.slug starts_with "type:" or
    tag.slug starts_with "cat:"  or
    tag.slug starts_with "status:"
  ) %}
    {% set display_tags = display_tags + [tag] %}
  {% endif %}
{% endfor %}

{# 表示用タグのみ出力 #}
{% if display_tags %}
  <div class="post-tags">
    {% for tag in display_tags %}
      <a href="{{ tag.tag_url }}" class="post-tag"># {{ tag.name }}</a>
    {% endfor %}
  </div>
{% endif %}
🌊 タグ設計のベストプラクティスまとめ

① プレフィックスで役割を明示: type: / cat: / status: など用途別にルールを作る
② スラッグは英数字・ハイフンのみ: URLに含まれるため日本語は避ける
③ 1記事に1つのtype:タグ: 複数のtype:タグを付けると判定が複雑になる
④ タグ一覧ドキュメントを作る: 運用者が迷わないよう命名規則をドキュメント化する


Section 6-3

ページネーション完全実装

HubSpotのブログ一覧・タグアーカイブページには、 current_page_num / last_page_num / blog_page_link() という変数・関数が自動的に使えます。 これを使ってSEO・UX両面で最適なページネーションを実装します。

ページネーションUIの種類

① スライドウィンドウ方式(推奨)

現在ページを中心に前後N件のページ番号を表示し、両端に省略(…)を入れる方式。大量ページがある場合に最適。

1
4
5
6
7
8
15
HubL — スライドウィンドウ ページネーション完全実装
{% if last_page_num > 1 %}

{# ウィンドウサイズ:現在ページの前後何件を表示するか #}
{% set win = 2 %}

<nav class="pagination" aria-label="ページネーション">
  <ul class="pagination__list">

    {# ← 前へ ボタン #}
    <li class="pagination__item pagination__item--prev
      {% if current_page_num == 1 %} is-disabled{% endif %}">
      {% if current_page_num > 1 %}
        <a href="{{ blog_page_link(current_page_num - 1) }}"
           aria-label="前のページへ">←</a>
      {% else %}
        <span aria-hidden="true">←</span>
      {% endif %}
    </li>

    {# 先頭ページ + 省略記号(現在が先頭から離れている場合) #}
    {% if current_page_num > win + 2 %}
      <li class="pagination__item">
        <a href="{{ blog_page_link(1) }}">1</a>
      </li>
      {% if current_page_num > win + 3 %}
        <li class="pagination__item pagination__item--ellipsis"
            aria-hidden="true"><span>…</span></li>
      {% endif %}
    {% endif %}

    {# ウィンドウ内のページ番号 #}
    {% for i in range(1, last_page_num + 1) %}
      {% if i >= current_page_num - win and i <= current_page_num + win %}
        <li class="pagination__item
          {% if i == current_page_num %} is-current{% endif %}">
          {% if i == current_page_num %}
            <span aria-current="page">{{ i }}</span>
          {% else %}
            <a href="{{ blog_page_link(i) }}"
               aria-label="{{ i }}ページ目">{{ i }}</a>
          {% endif %}
        </li>
      {% endif %}
    {% endfor %}

    {# 省略記号 + 末尾ページ(現在が末尾から離れている場合) #}
    {% if current_page_num < last_page_num - win - 1 %}
      {% if current_page_num < last_page_num - win - 2 %}
        <li class="pagination__item pagination__item--ellipsis"
            aria-hidden="true"><span>…</span></li>
      {% endif %}
      <li class="pagination__item">
        <a href="{{ blog_page_link(last_page_num) }}">{{ last_page_num }}</a>
      </li>
    {% endif %}

    {# 次へ → ボタン #}
    <li class="pagination__item pagination__item--next
      {% if current_page_num == last_page_num %} is-disabled{% endif %}">
      {% if current_page_num < last_page_num %}
        <a href="{{ blog_page_link(current_page_num + 1) }}"
           aria-label="次のページへ">→</a>
      {% else %}
        <span aria-hidden="true">→</span>
      {% endif %}
    </li>

  </ul>

  {# 件数情報の表示 #}
  <p class="pagination__info">
    {{ current_page_num }} / {{ last_page_num }} ページ
    (全 {{ contents.total_count }} 件)
  </p>

</nav>
{% endif %}

② 前へ・次へのみ(シンプル方式)

記事数が少ない・デザインをシンプルにしたい場合に適したパターンです。

HubL — シンプルな前後ナビゲーション
<nav class="pagination pagination--simple" aria-label="ページナビゲーション">
  {% if current_page_num > 1 %}
    <a class="pagination__prev"
       href="{{ blog_page_link(current_page_num - 1) }}">
      ← 新しい記事
    </a>
  {% endif %}

  {% if current_page_num < last_page_num %}
    <a class="pagination__next"
       href="{{ blog_page_link(current_page_num + 1) }}">
      古い記事 →
    </a>
  {% endif %}
</nav>

Section 6-4

HubDB の設計と活用

HubDB(ハブディービー)はHubSpot上で使えるクラウド型のリレーショナルデータベースです(Professional以上)。 スプレッドシートのような操作感でデータを管理でき、HubLからリアルタイムに取得して表示できます。

HubDB が適したユースケース

HubDB テーブル設計例:社員紹介テーブル

🗄️ テーブル名:team_members (スラッグ:team_members)
カラム名 スラッグ タイプ 説明
Row Name(自動)nameテキスト氏名(行の識別子)
表示名display_nameテキストページに表示する名前
役職titleテキスト役職・肩書き
部署department選択肢営業 / マーケ / 開発 / CS
顔写真avatar画像プロフィール写真
プロフィールbioリッチテキスト自己紹介文
メールemailテキスト表示用メールアドレス
LinkedInlinkedin_urlURLLinkedInプロフィールURL
表示順sort_order数値一覧での表示順序
表示するis_published真偽値false=非表示

HubDB データ取得の HubL 関数

関数引数説明
hubdb_table_rows(table, query) table: テーブルIDまたはスラッグ
query: クエリパラメータ(オプション)
テーブルの行データを配列で取得。最もよく使う関数。
hubdb_table_row(table, row_id) table / row_id: 行ID 特定の行を1件取得する。動的ページ生成で使用。
hubdb_table(table) table テーブルのメタ情報(列定義等)を取得する。

HubDB 基本的なデータ取得パターン

HubL — HubDB から社員一覧を取得して表示
{# ====== 基本的なデータ取得 ====== #}

{# 全件取得(表示フラグ=trueのみ、表示順でソート) #}
{% set members = hubdb_table_rows(
  "team_members",
  "orderBy=sort_order&is_published=true"
) %}

{# 件数の上限指定 #}
{% set members = hubdb_table_rows(
  "team_members",
  "orderBy=sort_order&limit=6"
) %}

{# 特定の部署のみ取得 #}
{% set sales_team = hubdb_table_rows(
  "team_members",
  "department=営業&orderBy=sort_order"
) %}

{# データを一覧表示 #}
{% if members %}
  <div class="team-grid">
    {% for member in members %}
      <div class="member-card">
        {% if member.avatar %}
          <img
            src="{{ member.avatar.url }}"
            alt="{{ member.display_name }}"
            width="200" height="200"
            loading="lazy">
        {% endif %}
        <h3>{{ member.display_name }}</h3>
        <p class="title">{{ member.title }}</p>
        {% if member.bio %}
          <div class="bio">{{ member.bio }}</div>
        {% endif %}
        {% if member.linkedin_url %}
          <a href="{{ member.linkedin_url }}"
             target="_blank" rel="noopener noreferrer">
            LinkedIn
          </a>
        {% endif %}
      </div>
    {% endfor %}
  </div>
{% else %}
  <p>メンバー情報がありません。</p>
{% endif %}

HubDB クエリパラメータ一覧

パラメータ使い方
orderByソート(カラム名、先頭に-で降順)orderBy=-sort_order(降順)
limit取得件数上限limit=6
offset取得開始位置offset=6(7件目から)
カラム名=値値での絞り込みis_published=true
カラム名__gtより大きい(greater than)price__gt=1000
カラム名__ltより小さい(less than)price__lt=5000
カラム名__contains文字列を含むname__contains=東京

Section 6-5

HubDB 動的ページ生成(Dynamic Pages)

HubDB の強力な機能がDynamic Pages(動的ページ)です。 HubDBの1行が自動的に1ページになる仕組みで、 /team/tanaka-taro のような個別詳細ページを DBのレコードから自動生成できます。

Dynamic Pages の仕組み

要素説明
ベースURL HubSpot管理画面のページ設定で指定(例:/team)
行のスラッグ HubDBの「Row name」フィールドがURLのパスになる(例:tanaka-taro → /team/tanaka-taro)
テンプレート Dynamic Pages用のテンプレートを1つ用意し、全レコードで共有する
データアクセス テンプレート内で dynamic_page_hubdb_row 変数から現在の行データを取得できる
HubL — Dynamic Pages テンプレートの実装

{% extends "./layouts/base.html" %}

{# dynamic_page_hubdb_row で現在の行データを取得 #}
{% set member = dynamic_page_hubdb_row %}

{# ページタイトルをメンバー名で上書き #}
{% block meta_title %}
  <title>{{ member.display_name }} | チーム | {{ site_settings.company_name }}</title>
{% endblock %}

{% block og_tags %}
  <meta property="og:title" content="{{ member.display_name }}">
  <meta property="og:type"  content="profile">
  {% if member.avatar %}
    <meta property="og:image" content="{{ member.avatar.url }}">
  {% endif %}
{% endblock %}

{% block main_content %}
  <article class="member-detail">

    {# パンくずリスト #}
    <nav class="breadcrumb">
      <ol>
        <li><a href="/">ホーム</a></li>
        <li><a href="/team">チーム</a></li>
        <li aria-current="page">{{ member.display_name }}</li>
      </ol>
    </nav>

    <div class="member-detail__profile">
      {% if member.avatar %}
        <img
          src="{{ member.avatar.url }}"
          alt="{{ member.display_name }}"
          width="240" height="240">
      {% endif %}

      <div>
        <h1>{{ member.display_name }}</h1>
        <p class="title">{{ member.title }}</p>
        {% if member.bio %}
          <div class="bio">{{ member.bio }}</div>
        {% endif %}
        {% if member.email %}
          <a href="mailto:{{ member.email }}">{{ member.email }}</a>
        {% endif %}
      </div>
    </div>

    {# 同じ部署のメンバー一覧(関連コンテンツ)#}
    {% set same_dept = hubdb_table_rows(
      "team_members",
      "department=" ~ member.department ~ "&is_published=true&orderBy=sort_order"
    ) %}
    {% if same_dept|length > 1 %}
      <section class="same-dept">
        <h2>{{ member.department }}のメンバー</h2>
        {% for m in same_dept %}
          {% if m.hs_id != member.hs_id %}
            <a href="/team/{{ m.hs_path }}">{{ m.display_name }}</a>
          {% endif %}
        {% endfor %}
      </section>
    {% endif %}

  </article>
{% endblock %}
ℹ️ Dynamic Pages の設定方法

HubSpot管理画面の「ウェブサイト → ウェブサイトページ」から新規ページを作成し、 テンプレートに Dynamic Pages 用テンプレートを選択します。 ページの設定で「データソース」に HubDB テーブルを指定すると、 テーブルの全行分のページが自動生成されます。 dynamic_page_hubdb_row は Dynamic Pages テンプレート専用の変数です。


Section 6-6

スマートコンテンツ(パーソナライズ)の実装

スマートコンテンツは、訪問者の属性(既知のコンタクト情報・デバイス・参照元・リスト所属)に応じて ページに表示するコンテンツを動的に変える機能です(Professional以上)。 HubSpotのCRM連携ならではのパーソナライズを実現できます。

スマートコンテンツの種類

コンタクトプロパティ

コンタクトのプロパティ値で変える

既知のコンタクト(フォーム入力済み)のCRMプロパティ値に基づいてコンテンツを変える。

会社名・業種・ライフサイクルステージ等
リスト(セグメント)

リスト所属でコンテンツを変える

HubSpotのコンタクトリスト(セグメント)に入っているかどうかで表示を変える。

「既存顧客リスト」「無料プランユーザー」等
デバイスタイプ

アクセス端末で変える

PC・スマートフォン・タブレット別に異なるコンテンツを表示する。

SP: アプリダウンロードCTA / PC: デモ申込CTA
参照元

流入元で変える

検索・SNS・メール・広告など参照元に応じてコンテンツを変える。

メール流入: 「おかえりなさい」バナー等

HubL でのパーソナライズ実装

スマートコンテンツはHubSpotの管理画面上でモジュールをスマート化する方法と、 HubLの contact 変数で直接コンタクトデータにアクセスする方法があります。

HubL — contact 変数によるパーソナライズ
{# ====== contact 変数:既知のコンタクト情報にアクセス ====== #}
{# 未知の訪問者の場合 contact は空(is not defined)になる #}

{# ① 既知 vs 未知の訪問者で表示を切り替える #}
{% if contact %}
  {# 既知のコンタクト:名前で挨拶 #}
  <p class="personalized-greeting">
    {{ contact.firstname }}さん、おかえりなさい!
  </p>
{% else %}
  {# 未知の訪問者:汎用のCTAを表示 #}
  <p>まずは無料で試してみませんか?</p>
{% endif %}

{# ② ライフサイクルステージで表示を切り替える #}
{% if contact.lifecyclestage == "customer" %}
  {# 既存顧客向け:アップセルCTA #}
  {% module "upsell_cta"
    path="../modules/cta-banner.module"
  %}
{% elif contact.lifecyclestage == "lead" or contact.lifecyclestage == "marketingqualifiedlead" %}
  {# リード向け:デモ申込CTA #}
  {% module "demo_cta"
    path="../modules/cta-banner.module"
  %}
{% else %}
  {# 未知 / サブスクライバー向け:資料DLフォーム #}
  {% module "download_cta"
    path="../modules/form-section.module"
  %}
{% endif %}

{# ③ 業種でコンテンツを切り替える #}
{% set industry_cases = {
  "製造業"   : "manufacturing",
  "IT"       : "it",
  "不動産"   : "realestate",
  "金融"     : "finance"
} %}

{% if contact and contact.industry and industry_cases[contact.industry] %}
  {% set case_tag = industry_cases[contact.industry] %}
  {% set related_cases = blog_posts("case", 3, 0, case_tag) %}
  {% if related_cases %}
    <section class="industry-cases">
      <h2>{{ contact.industry }}の導入事例</h2>
      {% for case in related_cases %}
        <a href="{{ case.absolute_url }}">{{ case.name }}</a>
      {% endfor %}
    </section>
  {% endif %}
{% endif %}

利用可能な contact のプロパティ

プロパティ内容値の例
contact.firstname名(ファーストネーム)太郎
contact.lastname姓(ラストネーム)田中
contact.emailメールアドレスtaro@example.com
contact.company会社名株式会社サンプル
contact.industry業種IT・通信
contact.lifecyclestageライフサイクルステージlead / customer 等
contact.jobtitle役職マーケティングマネージャー
contact.phone電話番号03-xxxx-xxxx
⚠️ contact 変数はProfessional以上 / Cookieが有効な既知のコンタクトのみ

contact 変数が使えるのはProfessional以上のプランで、かつ HubSpotのトラッキングクッキーが有効な既知のコンタクト(フォーム送信済み等)のみです。 未知の訪問者・Cookieをブロックしている訪問者では空になります。 必ず {% if contact %} で存在チェックをしてからアクセスしてください。


Section 6-7

ブログ高度活用パターン集

パターン①:トップページに複数種別の最新コンテンツを表示

HubL — トップページ:タブ切り替え対応の最新コンテンツ一覧
{# 各コンテンツ種別の最新記事を取得(1ブログ+タグ絞り込み)#}
{% set tab_config = [
  {"id": "all",       "label": "すべて",   "tag": ""},
  {"id": "blog",      "label": "ブログ",   "tag": "type:blog"},
  {"id": "seminar",   "label": "セミナー", "tag": "type:seminar"},
  {"id": "news",      "label": "お知らせ", "tag": "type:news"},
  {"id": "case",      "label": "導入事例", "tag": "type:case"}
] %}

{# タブナビゲーション #}
<div class="content-tabs" role="tablist">
  {% for tab in tab_config %}
    <button
      class="content-tab{% if loop.first %} is-active{% endif %}"
      role="tab"
      data-target="#tab-{{ tab.id }}"
      {% if loop.first %}aria-selected="true"{% endif %}>
      {{ tab.label }}
    </button>
  {% endfor %}
</div>

{# 各タブのコンテンツパネル #}
{% for tab in tab_config %}
  {% if tab.tag %}
    {% set tab_posts = blog_posts("default", 6, 0, tab.tag) %}
  {% else %}
    {% set tab_posts = blog_recent_posts("default", 6) %}
  {% endif %}

  <div
    id="tab-{{ tab.id }}"
    role="tabpanel"
    class="tab-panel{% if not loop.first %} is-hidden{% endif %}">
    {% if tab_posts %}
      <ul class="post-grid">
        {% for post in tab_posts %}
          <li>
            <a href="{{ post.absolute_url }}">
              {% if post.featured_image %}
                <img src="{{ post.featured_image }}"
                     alt="{{ post.featured_image_alt_text|default(post.name) }}"
                     loading="lazy">
              {% endif %}
              <p>{{ post.name }}</p>
              <time>{{ post.publish_date|datetimeformat("%Y.%m.%d") }}</time>
            </a>
          </li>
        {% endfor %}
      </ul>
    {% else %}
      <p>コンテンツがまだありません</p>
    {% endif %}
  </div>
{% endfor %}

{# タブ切り替えのJS(HubLで生成したIDを利用) #}
<script>
document.querySelectorAll('.content-tab').forEach(tab => {
  tab.addEventListener('click', () => {
    document.querySelectorAll('.content-tab, .tab-panel').forEach(el => {
      el.classList.remove('is-active');
      el.classList.add('is-hidden');
      el.removeAttribute('aria-selected');
    });
    tab.classList.add('is-active');
    tab.setAttribute('aria-selected', 'true');
    const panel = document.querySelector(tab.dataset.target);
    if (panel) {
      panel.classList.remove('is-hidden');
    }
  });
});
</script>

パターン②:前の記事・次の記事リンクの実装

HubL — 記事詳細の前後ナビゲーション
{# blog_post テンプレートで使える next_post_featured_image 等の変数 #}
<nav class="post-nav" aria-label="前後の記事">

  {% if next_post_featured_image or next_post_name %}
    <a class="post-nav__prev" href="{{ next_post_url }}"
       rel="prev">
      <span>← 次の記事</span>
      {% if next_post_featured_image %}
        <img src="{{ next_post_featured_image }}"
             alt="{{ next_post_name }}" loading="lazy">
      {% endif %}
      <p>{{ next_post_name }}</p>
    </a>
  {% endif %}

  {% if last_post_featured_image or last_post_name %}
    <a class="post-nav__next" href="{{ last_post_url }}"
       rel="next">
      <span>前の記事 →</span>
      {% if last_post_featured_image %}
        <img src="{{ last_post_featured_image }}"
             alt="{{ last_post_name }}" loading="lazy">
      {% endif %}
      <p>{{ last_post_name }}</p>
    </a>
  {% endif %}

</nav>

パターン③:ブログ記事数ゼロ時の分岐処理

HubL — データが0件・初期状態に対応したブログ一覧
{% set total = blog_total_post_count("default") %}

{% if total == 0 %}
  {# サイト全体でまだ記事がない場合 #}
  <div class="blog-empty">
    <p>まもなく記事を公開します。しばらくお待ちください。</p>
  </div>
{% elif contents|length == 0 %}
  {# タグページなどで該当記事が0件の場合 #}
  <div class="blog-empty">
    {% if content.tag %}
      <p>「{{ content.tag.name }}」の記事はまだありません。</p>
    {% else %}
      <p>記事がまだありません。</p>
    {% endif %}
    <a href="/blog">すべての記事を見る</a>
  </div>
{% else %}
  {# 記事が存在する通常表示 #}
  <ul class="post-grid">
    {% for post in contents %}
      {# ... #}
    {% endfor %}
  </ul>
{% endif %}

Section 6-8

第6章まとめ

📌 この章で押さえるべきポイント

ブログ設計の選択基準

Starterは1ブログ+タグ設計で種別管理。Professional以上は種別ごとに独立したブログを設置。複数ブログはIDを変数化して管理する。

タグ命名規則の重要性

type: / cat: / status: のプレフィックスで役割を分離。namespace パターンで複数属性を同時判定。表示用タグのみフィルタリングする実装が必須。

ページネーション実装

スライドウィンドウ方式がUX・SEO両面で最適。aria-current / aria-label を含めたアクセシブルな実装を心がける。件数情報の表示も忘れずに。

HubDB の設計と活用

社員紹介・製品一覧・FAQ等の構造化データ管理に最適。hubdb_table_rows() のクエリパラメータを使い、必要なデータだけを取得する。

Dynamic Pages

HubDBの1行が自動的に1ページに。dynamic_page_hubdb_row 変数で行データにアクセス。テンプレートは1つで全レコードを共有する。

スマートコンテンツ

contact 変数で既知コンタクトの情報にアクセス可能(Professional以上)。必ず存在チェックを入れ、ライフサイクルステージ・業種等で表示コンテンツを分岐する。

次章:第7章 フォーム・CTA・コンバージョン設計

HubSpotフォームの実装パターン・フォームCSS上書き・CTA設計・サンクスページ・コンバージョン計測まで徹底解説します。

第7章へ →