📘 HubSpot CMS Development Textbook — 2026 Edition
Chapter 6

Data utilization/dynamic content

A deep dive into blog functions, tag design strategies, complete pagination implementation, dynamic data management using HubDB, and personalization using smart content. This is a chapter to master HubSpot's data layer.

🎯 Target level:Intermediate to advanced
⏱ Estimated reading completion:90-120 minutes
🔗 Previous chapter:Chapter 5 Theme design and construction

Contents of this chapter

  1. Design concept of blog function (1 blog vs. multiple blogs)
  2. Tag Design Strategy: Category Alternatives in Starter Plan
  3. Fully implemented pagination
  4. Designing and leveraging HubDB
  5. HubDB Dynamic Pages
  6. Implementation of smart content (personalization)
  7. Blog advanced usage pattern collection
  8. Chapter 6 Summary
Section 6-1

Design concept of blog function (1 blog vs. multiple blogs)

The first decision in blog design for HubSpot CMS is “Should I operate multiple content types on one blog or use multiple blogs?”is. This choice depends on both your plan and your operational structure.

Blog design options by plan

planNumber of blogsRecommended design
Starter only one Type management with 1 blog + tags (tag design strategy described below is required)
Professional / Enterprise max 100 The ideal configuration is to set up separate blogs for each type of content.

Benefits and design of multiple blog configuration

If you use multiple blogs on Professional or higher, you can have separate URLs and templates for each content type.

Blog name (admin screen)URLPurposetemplate
Main blog/blogBlog articles/columnsblog-listing / blog-post
Seminar/seminarSeminar/event informationseminar-listing / seminar-post
notice/newsPress release/update informationnews-listing / news-post
case study/caseCase Studies/Customer Storiescase-listing / case-post
✅ Implementation points for multiple blog design

When using multiple blogs,blog_recent_posts() Specify the blog ID or blog slug in the first argument.
"default" is an alias for the default blog.
The blog ID is the URL of the HubSpot admin screen (app.hubspot.com/blog/PORTAL_ID/BLOG_ID/posts) to check.

HubL — Example implementation that handles multiple blogs
{# Define and manage blog ID as a variable (maintainability improved) #}
{% set blog_ids = {
  "main"    : "default",
  "seminar" : "12345678",   {# Seminar blog ID #}
  "news"    : "23456789",   {# Notification blog ID #}
  "case"    : "34567890"    {# Case blog ID #}
} %}

{# Get the latest articles from each blog and display them on the top page #}
{% 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) %}

{# Check number of seminars #}
{% set seminar_total = blog_total_post_count(blog_ids.seminar) %}
<p>全{{ seminar_total }}件のセミナー情報</p>{# Cross-search of multiple blogs (obtain the latest articles all at once) #}
{% 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) %}
⚠️ Cross-sorting of multiple blogs is not possible with HubL

If you want to display articles retrieved from multiple blogs sorted by publication date, HubL's sort You can use filters, butpublish_date is for unix timestamp Sorting directly requires some ingenuity. If you need a complete cross-searchImplementation with HubSpot Search API (JavaScript)Please consider.


Section 6-2

Tag Design Strategy: Category Alternatives in Starter Plan

HubSpot does not have a concept equivalent to WordPress' "categories", Tags also serve as categories. Especially if you can only use one blog with the Starter plan, Tag design is the most important element that affects the information design of the entire site.

Tag design principles: Separate roles with naming conventions

🏷️ Example of tag naming convention (clarify role with prefix)

Content type (type: prefix)
type:blog type:seminar type:news type:case type:whitepaper
Category (cat:prefix)
cat:marketing cat:sales cat:hubspot cat:beginner
Status/attribute (status: prefix)
status:featured status:pickup status:archive
Regular tag (keyword, no prefix)
HubSpot MA BtoB marketing lead acquisition

Implementation of tag judgment logic

HubL — Determine type/attribute by tag prefix
{# =======================================================
   Pattern for determining multiple roles at the same time from tags
   Execute at the beginning of article details (blog-post.html)
======================================================= #}

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

{% for tag in content.tag_list %}

  {# ① Content type determination #}
  {% if tag.slug starts_with "type:" %}
    {% set ns.content_type = tag.slug|replace("type:", "") %}

  {# ② Collect categories (add to array) #}
  {% elif tag.slug starts_with "cat:" %}
    {% set ns.categories = ns.categories + [tag.slug|replace("cat:", "")] %}

  {# ③ Status flag determination #}
  {% elif tag.slug == "status:featured" %}
    {% set ns.is_featured = true %}
  {% elif tag.slug == "status:pickup" %}
    {% set ns.is_pickup = true %}
  {% endif %}

{% endfor %}

{# Utilize judgment results #}
<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>

Tag navigation: control of display tags

Prefixed tag (type: / cat: / status:) is In many cases, you do not want to display it in the article's display tag list. Prepare a pattern to filter.

HubL — Extract only display tags
{# Extract only "display tags" excluding 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 %}

{# Output display tag only #}
{% 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 %}
🌊 Summary of best practices for tag design

① Clarify role with prefix: Create rules for different uses such as type: / cat: / status: etc.
② Only alphanumeric characters and hyphens can be used as slugs: Avoid Japanese as it is included in the URL
③ One type: tag per article: Attaching multiple type: tags will complicate the judgment.
④ Create a tag list document: Document naming conventions to avoid confusion for operators


Section 6-3

Fully implemented pagination

HubSpot's blog list/tag archive page includes: current_page_num / last_page_num / blog_page_link() variables and functions can be used automatically. Use this to implement pagination that is optimal for both SEO and UX.

Types of pagination UI

① Sliding window method (recommended)

A method that displays the N page numbers before and after the current page, and inserts an abbreviation (...) at both ends. Ideal if you have a large number of pages.

1
4
5
6
7
8
15
HubL — Full implementation of sliding window pagination
{% if last_page_num > 1 %}

{# Window size: How many items to display before and after the current page #}
{% set win = 2 %}

<nav class="pagination" aria-label="Pagination">
  <ul class="pagination__list">

    {# ← Previous button #}
    <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="Go to previous page">←</a>
      {% else %}
        <span aria-hidden="true">←</span>
      {% endif %}
    </li>

    {# First page + ellipsis (if current is far from the beginning) #}
    {% 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 %}

    {# Page number in window #}
    {% 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 }}Page number">{{ i }}</a>
          {% endif %}
        </li>
      {% endif %}
    {% endfor %}

    {# ellipsis + last page (if current is far from the end) #}
    {% 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 %}

    {# Next → button #}
    <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="Next page">→</a>
      {% else %}
        <span aria-hidden="true">→</span>
      {% endif %}
    </li>

  </ul>

  {# Display count information #}
  <p class="pagination__info">
    {{ current_page_num }} / {{ last_page_num }} ページ
    (全 {{ contents.total_count }} 件)
  </p>

</nav>{% endif %}

② Previous/Next only (simple method)

This pattern is suitable if you have a small number of articles or want to keep the design simple.

HubL — Simple back and forth navigation
<nav class="pagination pagination--simple" aria-label="Page navigation">
  {% 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

Designing and leveraging HubDB

HubDB(HubDB) is a cloud-based relational database that can be used on HubSpot (Professional or higher). Data can be managed with a spreadsheet-like operation, and data can be retrieved and displayed from HubL in real time.

Use cases for which HubDB is suitable

HubDB table design example: Employee introduction table

🗄️ Table name: team_members (slug: team_members)
Column name slug type explanation
Row Name (automatic)nametextName (row identifier)
display namedisplay_nametextName to display on page
posttitletextPosition/Title
DepartmentdepartmentchoicesSales / Marketing / Development / CS
face photoavatarimageprofile photo
profilebiorich textSelf-introduction text
emailemailtextDisplay email address
LinkedInlinkedin_urlURLLinkedIn profile URL
Display ordersort_ordernumerical valueDisplay order in list
displayis_publishedBoolean valuefalse=hidden

HubL functions for HubDB data retrieval

functionargumentexplanation
hubdb_table_rows(table, query) table: table ID or slug
query: query parameters (optional)
Get table row data as an array. Most frequently used functions.
hubdb_table_row(table, row_id) table / row_id: row ID Get one specific row. Used in dynamic page generation.
hubdb_table(table) table Obtain table meta information (column definitions, etc.).

HubDB basic data retrieval patterns

HubL — Get and display a list of employees from HubDB
{# ====== Basic data acquisition ====== #}

{# Get all results (only display flag = true, sort by display order) #}
{% set members = hubdb_table_rows(
  "team_members",
  "orderBy=sort_order&is_published=true"
) %}

{# Specify upper limit of number of items #}
{% set members = hubdb_table_rows(
  "team_members",
  "orderBy=sort_order&limit=6"
) %}

{# Get only specific departments #}
{% set sales_team = hubdb_table_rows(
  "team_members",
  "department=Sales&orderBy=sort_order"
) %}

{# List data #}
{% 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 query parameter list

parametersHow to useexample
orderBySort (column name, - in descending order)orderBy=-sort_order(descending order)
limitMaximum number of acquisitionslimit=6
offsetAcquisition start positionoffset=6(From the 7th item)
column name=valueFilter by valueis_published=true
Column name__gtgreater thanprice__gt=1000
Column name__ltless thanprice__lt=5000
Column name__containscontains stringname__contains=東京

Section 6-5

HubDB Dynamic Pages

HubDB's powerful featuresDynamic Pagesis. One row in HubDB automatically becomes one page, /team/tanaka-taro individual details page like It can be automatically generated from DB records.

How Dynamic Pages works

elementexplanation
base url Specified in the page settings of the HubSpot admin screen (e.g. /team)
line slug HubDB's "Row name" field becomes the URL path (e.g. tanaka-taro → /team/tanaka-taro)
template Prepare one template for Dynamic Pages and share it with all records
data access within the template dynamic_page_hubdb_row You can get the current row data from variables
HubL — Dynamic Pages template implementation

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

{# Get current row data with dynamic_page_hubdb_row #}
{% set member = dynamic_page_hubdb_row %}

{# Overwrite page title with member name #}
{% 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">

    {# Breadcrumb #}
    <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>

    {# List of members in the same department (related content) #}
    {% 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 %}
ℹ️ How to set up Dynamic Pages

Create a new page from "Website → Website Page" in the HubSpot admin screen, Select Dynamic Pages template as the template. If you specify the HubDB table in "Data Source" in the page settings, A page for all rows of the table will be automatically generated. dynamic_page_hubdb_row is a variable specific to Dynamic Pages templates.


Section 6-6

Implementation of smart content (personalization)

smart contentdepending on visitor attributes (known contact information, device, referrer, list affiliation) This is a function that dynamically changes the content displayed on the page (Professional or higher). You can achieve personalization unique to HubSpot's CRM integration.

Types of smart content

contact properties

Change by contact property value

Vary content based on CRM property values ​​of known contacts (form filled).

Company name, industry, life cycle stage, etc.
list (segment)

Change content based on list affiliation

The display changes depending on whether you are in HubSpot's contact list (segment).

“Existing customer list” “Free plan users” etc.
device type

Change with access terminal

Display different content for PCs, smartphones, and tablets.

SP: App download CTA / PC: Demo application CTA
Reference source

change depending on the source of inflow

Change the content depending on the reference source such as search, SNS, email, advertisement, etc.

Email inflow: “Welcome back” banners, etc.

Personalization implementation in HubL

Smart content is how to make modules smart on the HubSpot admin screen, HubL's contact There is a way to access contact data directly with variables.

HubL — Personalization with the contact variable
{# ====== contact variable: access known contact information ====== #}
{# For unknown visitors, contact will be empty (is not defined) #}

{# ① Switch display between known vs. unknown visitors #}
{% if contact %}
  {# Known contacts: Greet by name #}
  <p class="personalized-greeting">
    {{ contact.firstname }}さん、おかえりなさい!
  </p>{% else %}
  {# Unknown visitor: Show generic CTA #}
  <p>まずは無料で試してみませんか?</p>{% endif %}

{# ② Switch display by lifecycle stage #}
{% if contact.lifecyclestage == "customer" %}
  {# For existing customers: Upsell CTA #}
  {% module "upsell_cta"
    path="../modules/cta-banner.module"
  %}
{% elif contact.lifecyclestage == "lead" or contact.lifecyclestage == "marketingqualifiedlead" %}
  {# For leads: Demo application CTA #}
  {% module "demo_cta"
    path="../modules/cta-banner.module"
  %}
{% else %}
  {# Unknown / For subscribers: Material DL form #}
  {% module "download_cta"
    path="../modules/form-section.module"
  %}
{% endif %}

{# ③ Switch content by industry #}
{% set industry_cases = {
  "Manufacturing"   : "manufacturing",
  "IT"       : "it",
  "real estate"   : "realestate",
  "Finance"     : "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 %}

Available contact properties

propertiesContentExample value
contact.firstnamefirst nameTaro
contact.lastnameSurname (last name)Tanaka
contact.emailemail addresstaro@example.com
contact.companyCompany NameSample Co., Ltd.
contact.industryIndustryIT/Communication
contact.lifecyclestagelife cycle stagelead / customer etc.
contact.jobtitlepostmarketing manager
contact.phonetelephone number03-xxxx-xxxx
⚠️ contact variable must be Professional or higher / known contacts with cookies enabled only

contact Variables can be used on Professional or higher plans, and Only known contacts with HubSpot tracking cookies enabled (e.g., form submissions)is. It will be empty for unknown visitors and visitors who have blocked cookies. surely {% if contact %} Please check the existence before accessing.


Section 6-7

Blog advanced usage pattern collection

Pattern 1: Displaying multiple types of latest content on the top page

HubL — Top page: Latest content list that supports tab switching
{# Get the latest articles of each content type (1 blog + tag filtering) #}
{% set tab_config = [
  {"id": "all",       "label": "all",   "tag": ""},
  {"id": "blog",      "label": "Blog",   "tag": "type:blog"},
  {"id": "seminar",   "label": "Seminar", "tag": "type:seminar"},
  {"id": "news",      "label": "notice", "tag": "type:news"},
  {"id": "case",      "label": "Introduction example", "tag": "type:case"}
] %}

{# Tab navigation #}
<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>{# Content panel for each tab #}
{% 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 %}

{# Tab switching JS (using ID generated by HubL) #}
<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>

Pattern 2: Implementation of previous article/next article links

HubL — Back and forward navigation for article details
{# Variables such as next_post_featured_image that can be used in the blog_post template #}
<nav class="post-nav" aria-label="Previous and subsequent articles">

  {% 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>

Pattern 3: Branch processing when the number of blog articles is zero

HubL — List of blogs with 0 data/initial state
{% set total = blog_total_post_count("default") %}

{% if total == 0 %}
  {# If there are no articles yet for the whole site #}
  <div class="blog-empty">
    <p>まもなく記事を公開します。しばらくお待ちください。</p>
  </div>{% elif contents|length == 0 %}
  {# If there are 0 corresponding articles on the tag page etc. #}
  <div class="blog-empty">
    {% if content.tag %}
      <p>「{{ content.tag.name }}」の記事はまだありません。</p>
    {% else %}
      <p>記事がまだありません。</p>
    {% endif %}
    <a href="/blog">すべての記事を見る</a>
  </div>{% else %}
  {# Normal display with article #}
  <ul class="post-grid">
    {% for post in contents %}
      {# ... #}
    {% endfor %}
  </ul>{% endif %}

Section 6-8

Chapter 6 Summary

📌 Key points to keep in mind in this chapter

Blog design selection criteria

Starter manages types with 1 blog + tag design. For Professionals and above, separate blogs are set up for each type. Multiple blogs are managed by converting IDs into variables.

Importance of tag naming conventions

Separate roles with type: / cat: / status: prefix. Simultaneously determine multiple attributes using namespace patterns. Implementation that filters only display tags is required.

Pagination implementation

The sliding window method is optimal for both UX and SEO. Make sure to have an accessible implementation including aria-current / aria-label. Don't forget to display the count information.

Designing and leveraging HubDB

Ideal for managing structured data such as employee introductions, product lists, FAQs, etc. Use the query parameters of hubdb_table_rows() to retrieve only the necessary data.

Dynamic Pages

One row in HubDB automatically becomes one page. Access row data with the dynamic_page_hubdb_row variable. One template shares all records.

smart content

Information on known contacts can be accessed using the contact variable (Professional and above). Be sure to check the presence and branch the displayed content based on life cycle stage, industry, etc.

Next chapter: Chapter 7 Form/CTA/Conversion Design

We will thoroughly explain HubSpot form implementation patterns, form CSS overrides, CTA design, thank you pages, and conversion measurement.

Go to Chapter 7 →