🟢HubSpot 開発者向け実践教科書 — 2026年版 Developer Edition
Chapter 6  ·  CMS Module Design

CMS Hub 開発——
モジュール設計

fields.json によるフィールド定義・module.html での HubL 実装・meta.json の設定・カスタムモジュールのベストプラクティスまで。再利用可能なコンポーネントを設計・実装する技術を学ぶ。

fields.json 完全ガイド
HubL module.html
所要時間:約90分
6-1 モジュールとは何か

HubSpot モジュールの役割と、テンプレートとの違いを理解する。

🧩 モジュールの定義

モジュールはドラッグ&ドロップエディタで配置できる再利用可能なコンポーネントです。開発者がコードと編集フィールドを定義し、マーケターはノーコードで内容を変更できます。ヒーローバナー・CTA セクション・カード一覧・テスティモニアルなど、あらゆる UI パーツをモジュール化できます。

📄 テンプレートとの違い

テンプレートはページ全体の骨格(1ページに1つ)。モジュールはテンプレート内に複数配置できる部品。テンプレートが「間取り図」ならモジュールは「家具」のイメージ。

🔁 再利用性

1つのモジュールを作れば、複数テンプレートで共有できます。モジュールを更新すると、配置されている全ページに反映されます(グローバルモジュールの場合)。

6-2 モジュールのディレクトリ構造

モジュールは .module 拡張子のディレクトリで構成される。

hero-banner.module/
fields.json← 編集可能フィールドの定義(必須)
module.html← HubL テンプレート(必須)
module.css← モジュール固有のスタイル(任意)
module.js← モジュール固有の JS(任意)
meta.json← モジュールのメタ情報(必須)
ファイル命名規則: ディレクトリ名の .module が識別子です。例えば hero-banner.module/ の場合、HubSpot は自動的にこれをモジュールとして認識します。中のファイル名は固定です(fields.jsonmodule.htmlmeta.json は変更不可)。
6-3 fields.json — フィールド定義の完全ガイド

マーケターが編集できるフィールドをすべて fields.json で定義する。

type用途戻り値
text1行テキスト文字列
richtextリッチテキストエディタHTML 文字列
image画像選択オブジェクト(src, alt, width, height)
urlURL・リンク先オブジェクト(url, type, open_in_new_tab)
choiceドロップダウン選択選択値(文字列)
booleanオン/オフ トグルtrue / false
number数値入力数値
colorカラーピッカーオブジェクト(color, opacity)
fontフォント設定オブジェクト(font, size, bold, italic…)
groupフィールドのグルーピング(リスト化可能)オブジェクト配列
hubdbrowHubDB 行の選択行 ID
ctaHubSpot CTA の選択CTA ID
formHubSpot フォームの選択フォーム情報
blogブログの選択ブログ ID
fields.json — ヒーローバナーモジュールの例
[ { "id": "heading", "name": "heading", "label": "見出し", "type": "text", "default": "あなたのビジネスを加速させる", "required": true }, { "id": "subheading", "name": "subheading", "label": "サブ見出し", "type": "text", "default": "HubSpot で顧客管理を一元化しましょう" }, { "id": "background_image", "name": "background_image", "label": "背景画像", "type": "image", "default": { "src": "", "alt": "" } }, { "id": "cta_button", "name": "cta_button", "label": "CTAボタン", "type": "group", "children": [ { "id": "text", "name": "text", "label": "ボタンテキスト", "type": "text", "default": "無料で始める" }, { "id": "url", "name": "url", "label": "リンク先", "type": "url", "default": { "url": "", "open_in_new_tab": false } }, { "id": "style", "name": "style", "label": "スタイル", "type": "choice", "choices": [["primary","プライマリ"],["secondary","セカンダリ"],["outline","アウトライン"]], "default": "primary" } ] }, { "id": "layout", "name": "layout", "label": "レイアウト", "type": "choice", "choices": [["center","中央揃え"],["left","左揃え"],["right","右揃え"]], "default": "center" }, { "id": "overlay_opacity", "name": "overlay_opacity", "label": "オーバーレイの不透明度 (%)", "type": "number", "default": 50, "min": 0, "max": 100 } ]
6-4 module.html — HubL テンプレートの実装

fields.json で定義したフィールドを module.html で HubL として利用する。

module.html — ヒーローバナー
{# module_attribute でフィールド値を取得 #} <section class="hero-banner hero-layout-{{ module.layout }}" style=" {% if module.background_image.src %} background-image: url({{ module.background_image.src | resize_image_url(1440, 800) }}); {% endif %} " > {# 背景オーバーレイ #} <div class="hero-overlay" style="opacity: {{ module.overlay_opacity / 100 }}" ></div> <div class="hero-content"> {% if module.heading %} <h1 class="hero-heading">{{ module.heading }}</h1> {% endif %} {% if module.subheading %} <p class="hero-subheading">{{ module.subheading }}</p> {% endif %} {% if module.cta_button.url.url %} <a href="{{ module.cta_button.url.url }}" class="btn btn-{{ module.cta_button.style }}" {% if module.cta_button.url.open_in_new_tab %}target="_blank" rel="noopener"{% endif %} > {{ module.cta_button.text }} </a> {% endif %} </div> </section>
module.html — group フィールドのリスト表示(カード一覧)
{# fields.json で type: group, occurrence: {min:1, max:6} を設定したリピートグループ #} <div class="card-grid card-cols-{{ module.columns }}"> {% for card in module.cards %} <div class="card"> {% if card.icon %} <div class="card-icon">{{ card.icon }}</div> {% endif %} <h3 class="card-title">{{ card.title }}</h3> <p class="card-body">{{ card.body }}</p> {% if card.link.url %} <a href="{{ card.link.url }}" class="card-link">{{ card.link_text | default('詳しく見る') }}</a> {% endif %} </div> {% endfor %} </div>
6-5 meta.json — モジュールのメタ情報

モジュールの表示名・使用可能なコンテキスト・アイコンなどを設定する。

meta.json
{ "label": "ヒーローバナー", "css_assets": [{ "path": "module.css" }], "js_assets": [], "other_assets": [], "smart_type": "NOT_SMART", "icon": "heroicons/solid/photograph", "is_available_for_new_content": true, // このモジュールが使えるコンテキストを制限 "content_types": ["SITE_PAGE", "LANDING_PAGE"], // DnD テンプレートでのみ使用可能にする "host_template_types": ["PAGE", "BLOG_POST"] }
content_types の値説明
SITE_PAGEサイトページ
LANDING_PAGEランディングページ
BLOG_POSTブログ記事
BLOG_LISTINGブログ一覧
EMAILマーケティングメール
6-6 module.css / module.js のベストプラクティス

モジュール固有のスタイルと JS を適切にスコープする。

module.css — スコープ付きスタイル
/* モジュール固有のスタイルは .hero-banner クラスでスコープ */ .hero-banner { position: relative; min-height: 560px; display: flex; align-items: center; justify-content: center; background-size: cover; background-position: center; overflow: hidden; } .hero-overlay { position: absolute; inset: 0; background: #000; } .hero-content { position: relative; z-index: 1; text-align: center; max-width: 720px; padding: 40px 24px; color: #fff; } .hero-layout-left .hero-content { text-align: left; } .hero-layout-right .hero-content { text-align: right; } @media (max-width: 768px) { .hero-banner { min-height: 320px; } }
module.js — 初期化パターン
// DOMContentLoaded を待たずに即時実行(HubSpot の非同期ロードに対応) (function() { function initHeroBanner() { const banners = document.querySelectorAll('.hero-banner'); banners.forEach(function(banner) { // すでに初期化済みならスキップ if (banner.dataset.initialized) return; banner.dataset.initialized = 'true'; // 初期化処理... }); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initHeroBanner); } else { initHeroBanner(); } })();
⚠ グローバル変数の汚染に注意: module.js は即時関数(IIFE)でラップし、グローバルスコープを汚染しないようにしてください。同じページに複数のモジュールが配置される場合、変数名が衝突する可能性があります。
6-7 この章のまとめ

✅ Chapter 6 チェックリスト

  • モジュールの4ファイル構成(fields.json / module.html / module.css / meta.json)を理解した
  • fields.json の主要フィールド型(text / image / url / choice / group)を使いこなせる
  • group フィールドでリピート可能なコンポーネントを設計できる
  • module.html で module.フィールド名 を HubL で参照できる
  • meta.json で使用可能なコンテキストを適切に制限できる
  • IIFE パターンで JS をスコープし、グローバル汚染を防げる
次章(Chapter 7)について:ワークフロー拡張とカスタムコードアクションを学びます。Node.js による Custom Code Action・Serverless Functions・Run Agent ステップを解説します。