📘 HubSpot CMS Development Textbook — 2026 Edition
Chapter 1

Big picture of HubSpot CMS
Development environment construction

From what HubSpot CMS is, how it differs from other CMSs, and how to choose a plan, to acquiring the development portal, setting up the CLI, designing the theme directory, and defining design tokens using theme.json. This chapter provides everything you need to start developing.

🎯 Target level:Beginner to intermediate
⏱ Estimated reading completion:60-90 minutes
🔗 Next chapter:Chapter 2 HubL Basics
🆕 March 2026 version — CLI v8 / Node.js 20 compatible

Contents of this chapter

  1. What is HubSpot CMS? Comparison with other CMS
  2. Concept of plan comparison and selection
  3. Obtaining a development portal (Developer Account)
  4. Structure of the management screen and screens used by developers
  5. Building a development environment (Node.js / CLI / editor)
  6. Theme directory design
  7. Designing design tokens with theme.json
  8. Global CSS design (ITCSS approach)
  9. Upload your first theme
  10. Chapter 1 Summary
Section 1-1

What is HubSpot CMS? Comparison with other CMS

HubSpot CMS (Content Management System) A cloud-based website construction and management platform provided by HubSpot. The biggest difference from other CMS is thatExists on the same platform as CRM (customer management), marketing, and sales toolsThat's it. A contact record is created the moment someone visits your site and submits a form. The workflow moves and notifications are sent to the sales person in charge—this all-in-one system is its greatest strength.

Comparison of characteristics of HubSpot CMS and other CMS

🟠 HubSpot CMS

  • Fully integrated with CRM/MA
  • No infrastructure management required (SaaS)
  • Global CDN installed as standard
  • HubL template language
  • Personalize content
  • Easy-to-operate UI for marketers
  • Monthly cost is high

🔵 WordPress

  • No.1 share in the world・A wealth of information
  • Extend functionality with plugins
  • Requires server management
  • Security measures required
  • CRM requires separate integration
  • High degree of freedom and customization
  • Low running costs

🟣 Headless CMS

  • Separate content and UI
  • Front is optional FW (Next etc.)
  • Maximum development freedom
  • Highly dependent on engineers
  • CRM requires separate integration
  • Sometimes it is difficult for marketers to use
  • The configuration tends to be complicated

When to choose HubSpot CMS

💡 HubSpot CMS limitations you should understand as a developer

① HubL is the only template language:Front-end frameworks such as React, Vue, and Next.js cannot be used directly. HubL + raw HTML/CSS/JS is the basics.
② Server-side custom processing not possible:Backend code such as PHP/Node.js cannot be run. Dynamic processing will be replaced by HubL, HubDB, and JavaScript API.
③ Direct access to the file system is not possible:Files can only be manipulated through the CLI or admin interface.


Section 1-2

Concept of plan comparison and selection

The features available with HubSpot CMS vary greatly depending on the usage plan. Check the client's plan before development,What can and cannot be usedUnderstanding this is the starting point for design.

function Starter Professional Enterprise
Custom theme/module development
Blog function (articles/tags)✅ 1 blog✅ Multiple✅ Multiple
HubDB (Cloud DB)
Smart content (personalized)
Dynamic Pages (HubDB linked pages)
A/B test
Multilingual content
Custom Object (CRM)
sandbox environment⚠️ Standard✅ For developers
content partitioning

Design changes depending on the plan

design elementsAlternatives in StarterProfessional or above
Content type management Manage types with 1 blog + tag prefix (type:blog / type:seminar, etc.) Set up independent blogs for each type
List of products/case studies Manage with alternative/static HTML pages using blog tag function Dynamic management with HubDB tables
Change content with user attributes Basically not possible (same content for all visitors) Display by life cycle and industry with smart content
⚠️ Don't start designing without checking the plans

Even if there is a requirement that “I want to create a staff introduction page using HubDB”, HubDB cannot be used if the client is on the Starter plan. Be sure to check the plan immediately after receiving the order and before starting the design. If there are features that cannot be used, we will suggest alternatives or consider upgrading your plan.Please. You can check your plan from Accounts & Subscriptions in your HubSpot admin screen.


Section 1-3

Obtaining a development portal (Developer Account)

At HubSpotFree Developer Accountcan be created. The Developer Account is a sandbox environment where you can use almost all the functions equivalent to production. This is an essential environment for development and verification before touching the client's portal.

Steps to obtain a Developer Account

🔗Developer Account creation URL

app.hubspot.com/signup-hubspot/developers Create an account from.
Register as a "developer account" separate from your regular HubSpot account. If you have an existing HubSpot account, you may not be able to create one using the same email address. We recommend that you prepare an email address for development purposes.

itemContent
costFree (no fee for the Developer Account itself)
Main functions that can be usedTheme/module development/HubDB/form/blog/page creation
limitEmail sending and some marketing functions are limited. Actual operation as a public site is not possible.
Number of portalsCan create up to 10 test portals
data retentionAccess it regularly as it may be deleted if there is no activity for 90 days

Issuing a Personal Access Key

A Personal Access Key is required to access the portal via the CLI. After creating a Developer Account, issue it using the following steps.

⚠️ Careful handling of Personal Access Key

A Personal Access Key has full access to the portal. Pasting to Slack, email, or GitHub is strictly prohibited. Manage with password managers such as 1Password and Bitwarden. If you want to share it with your team, please use the SecretSharing tool (e.g. Keybase). If it is leaked, we will immediately invalidate it and reissue it.


Section 1-4

Structure of the management screen and screens used by developers

HubSpot's admin screen is multi-functional, so it's easy to get lost at first. We will organize the screens that you should at least understand as a developer.

screen namemenu locationWhen used by developers
design manager Marketing → Files and Templates → Design Manager Check templates, modules, CSS, edit and preview on browser
file manager Marketing → Files and Templates → File Manager Upload image/PDF/confirm file URL
website page Marketing → Website → Website Page Check the operation of the created template and page settings (canonical, OGP, etc.)
Blog Marketing → Website → Blog Blog settings, linking with templates, tag management
HubDB Marketing → Files and Templates → HubDB Creating tables, designing schemas, and testing data
form Marketing → Form Confirm form ID, set hidden field, set post-send action
Settings → Domains and URLs ⚙️ Settings → Website → Domains and URLs Check domain connection/www settings/page URL
Settings → Tracking code ⚙️ Settings → Tracking and Analytics GA4/GTM ID settings/HubSpot tracking confirmation

Section 1-5

Building a development environment (Node.js / CLI / editor)

Installing the necessary tools

toolVersion requirementsPurpose
Node.js20.x or higher (required)Node 20 is a minimum requirement for HubSpot CLI v8.0.0 and later. Management with nvm is recommended. Support for Node 18 ends in October 2025.
@hubspot/cliv8.0.0 or higherv8 will be effective from February 2026. The command system hs cms upload Changed to etc. The old v5 series is not recommended for new projects.
VS CodeanyRecommended editor. Includes HubL extensions (described later).
Git2.x or higherVersion control. Required for CI/CD with GitHub Actions.
Terminal — All steps to build the environment
# ===== Step 1: Check Node.js version (requires 20 or higher) =====
$ node --version
v20.11.0 ← Confirm that it is 20 or higher (18.x is NG)

# Version control using nvm (recommended)
$ nvm install 20$ nvm use 20# ===== Step 2: HubSpot CLI installation =====
$ npm install -g @hubspot/cli
$ hs --version
5.x.x ← Installation confirmation

# ===== Step 3: Create project folder =====
$ mkdir my-company-hubspot
$ cd    my-company-hubspot
$ git init$ npm init -y

# ===== Step 4: HubSpot CLI authentication =====
$ hs init? Enter a name for this account: my-company-dev
? Enter your HubSpot portal ID: 12345678
? How would you like to authenticate?
  ❯ Personal Access Key (recommended)
? Enter your personal access key: eu1-xxxx-xxxx-xxxx
✔ Authenticated! Config saved to hubspot.config.yml

# ===== Step 5: Create .gitignore =====
$ echo "hubspot.config.yml" >> .gitignore$ echo "node_modules/"      >> .gitignore$ echo ".DS_Store"          >> .gitignore# ===== Step 6: Generate theme template =====
$ mkdir src$ hs create theme my-theme --path=./src
✔ Theme created at ./src/my-theme

Recommended settings for VS Code

.vscode/extensions.json — Recommended extensions
{
  "recommendations": [
    "HubSpot.hubl",              // HubL syntax highlighting/completion
    "esbenp.prettier-vscode",    // code formatter
    "dbaeumer.vscode-eslint",    // JavaScript lint
    "bradlc.vscode-tailwindcss", // When using Tailwind
    "streetsidesoftware.code-spell-checker" // spell check
  ]
}
.vscode/settings.json — HubL file settings
{
  // Recognize .html files as HubL
  "files.associations": {
    "*.html": "hubl"
  },
  // Format on save
  "editor.formatOnSave": true,
  // Indentation is 2 spaces
  "editor.tabSize": 2,
  "editor.insertSpaces": true
}

Section 1-6

Theme directory design

From codebase analysis of an actual project (a large-scale project with 741 modules and 964 templates), We have derived the optimal directory structure that works in practice. The following is the recommended standard configuration.

Recommended directory structure

src/my-theme/ ├── theme.json # ★ Theme settings/design token definition │ ├── templates/ # page template │ ├── page.html # Generic page │ ├── landing-page.html # LP (no header/footer) │ ├── blog-listing.html # Blog list │ ├── blog-post.html # Blog article details │ ├── search-results.html # Search results │ └── error-page.html # 404 error page │ ├── layouts/ # Inherited layout │ └── base.html # ★ Inheritance source of all templates │ ├── partials/ # Reuse partial │ ├── header.html # global header │ ├── footer.html # global footer │ ├── head.html # Common elements within the <head> tag │ └── blog-sidebar.html # Blog sidebar │ ├── modules/ # custom module │ ├── hero-banner.module/ │ │ ├── fields.json # ★ Field definition │ │ ├── module.html # template │ │ ├── module.css # style │ │ ├── module.js # script (optional) │ │ └── meta.json # Module meta information │ ├── card-grid.module/ │ ├── cta-banner.module/ │ └── faq-accordion.module/ │ ├── css/ # Global CSS (ITCSS configuration) │ ├── 01-settings.css # CSS variables/design tokens │ ├── 02-generic.css # reset/normalize │ ├── 03-elements.css # Plain HTML elements (h1 to h6, p, a, etc.) │ ├── 04-objects.css # Layout pattern (.container, .grid, etc.) │ ├── 05-components.css # UI components (.btn, .badge, etc.) │ ├── 06-utilities.css # Utility class (.mt-4, .hidden, etc.) │ └── main.css # Entry point to combine the above with @import │ ├── js/ # Global JavaScript │ ├── main.js # entry point │ └── utils.js # Common utility functions │ ├── images/ # Static images within the theme (logos, etc.) │ └── logo.svg │ ├── .hsignore # Define CLI sync exclusion file └── README.md # Theme explanation/development procedure (excluded with .hsignore)
✅ Important design points learned from actual project analysis

From an analysis of a large project with 741 modules and 964 templates, Designed for easy management even as the number of modules increasesI found the following to be important for this purpose.
① The module is .module directoryManage by unit (5 files 1 set)
② CSS is managed in a layered structure similar to ITCSS, and module-specific styles are separated into module.css.
③ Templates are clearly separated by page type (general purpose/LP/blog/error page)


Section 1-7

Designing design tokens with theme.json

theme.json is the HubSpot theme's configuration file. The values defined here can be changed with no code on the HubSpot admin screen. It can be referenced as a CSS variable in all templates and all modules. “The starting point for unified management of design”This is the most important file.

Overall structure of theme.json

🎨 Color settings

  • Primary/Secondary color
  • Text/background/border color
  • Status color (success/warning/error)
  • Works with color picker in HubSpot admin screen

✍️ Typography

  • Heading/body font family
  • font size scale
  • font weight
  • line-height

📐 Spacing

  • Section top and bottom margins
  • maximum width of container
  • grid gap
  • common padding

🔲 UI settings

  • border radius
  • box shadow
  • button style
  • responsive breakpoints
theme.json — Recommended configuration derived from actual projects
{
  "label": "My Company Theme",
  "preview_path": "./templates/page.html",
  "screenshot_path": "./images/screenshot.png",
  "enable_domain_stylesheets": false,
  "hide_all_default_modules": true,
  // ↑ Do not display HubSpot default module in page editor
  // Set to true if you want to use only custom modules

  "fields": [

    // ===== Color =====
    {
      "type": "group",
      "name": "colors",
      "label": "Color settings",
      "children": [
        {
          "type": "color",
          "name": "primary_color",
          "label": "Primary color",
          "default": { "color": "#0052CC", "opacity": 100 }
        },
        {
          "type": "color",
          "name": "secondary_color",
          "label": "Secondary Color",
          "default": { "color": "#FF5630", "opacity": 100 }
        },
        {
          "type": "color",
          "name": "text_color",
          "label": "Text Color",
          "default": { "color": "#2d3748", "opacity": 100 }
        },
        {
          "type": "color",
          "name": "bg_color",
          "label": "Page background color",
          "default": { "color": "#ffffff", "opacity": 100 }
        }
      ]
    },

    // ===== Typography =====
    {
      "type": "group",
      "name": "typography",
      "label": "typography",
      "children": [
        {
          "type": "font",
          "name": "heading_font",
          "label": "Heading font",
          "default": {
            "font": "Noto Sans JP",
            "font_set": "GOOGLE",
            "variant": "700",
            "size": "36px",
            "color": "#1a202c"
          }
        },
        {
          "type": "font",
          "name": "body_font",
          "label": "Body font",
          "default": {
            "font": "Noto Sans JP",
            "font_set": "GOOGLE",
            "variant": "400",
            "size": "16px",
            "color": "#2d3748"
          }
        }
      ]
    },

    // ===== Spacing Layout =====
    {
      "type": "group",
      "name": "layout",
      "label": "Layout settings",
      "children": [
        {
          "type": "number",
          "name": "container_max_width",
          "label": "Container maximum width (px)",
          "default": 1200
        },
        {
          "type": "number",
          "name": "section_padding_v",
          "label": "Section top and bottom margins (px)",
          "default": 80
        },
        {
          "type": "number",
          "name": "border_radius",
          "label": "Rounded corners (px)",
          "default": 8
        }
      ]
    }
  ]
}

Expand theme.json values ​​as CSS variables

theme.json The values ​​defined in are expanded as CSS variables using HubL. All templates <head> Define it within or at the beginning of your global CSS.

css/01-settings.css (or <style> in layouts/base.html)
/* Expand theme.json values as CSS variables
   Access with HubL's theme variable */
:root {
  /* ===== Color ===== */
  --color-primary   : {{ theme.colors.primary_color.color }};
  --color-secondary : {{ theme.colors.secondary_color.color }};
  --color-text      : {{ theme.colors.text_color.color }};
  --color-bg        : {{ theme.colors.bg_color.color }};

  /* ===== Typography ===== */
  --font-heading    : {{ theme.typography.heading_font.font }}, sans-serif;
  --font-body       : {{ theme.typography.body_font.font }}, sans-serif;
  --font-size-base  : {{ theme.typography.body_font.size }};

  /* ===== Layout ===== */
  --container-max   : {{ theme.layout.container_max_width }}px;
  --section-padding : {{ theme.layout.section_padding_v }}px;
  --border-radius   : {{ theme.layout.border_radius }}px;

  /* ===== Breakpoint (fixed value/CSS variable cannot be used in a media query, so for reference only) ===== */
  --bp-sm  : 480px;
  --bp-md  : 768px;
  --bp-lg  : 1024px;
  --bp-xl  : 1280px;
}

Section 1-8

Global CSS design (ITCSS approach)

In code base analysis of actual projects, global CSS is close to ITCSS (Inverted Triangle CSS). It was confirmed that it was designed with a layered structure. ITCSS isDefine CSS in the order of "abstract → concrete" and "wide range → local"With design philosophy, Prevents style override issues and level of detail conflicts.

ITCSS layer structure

Settings(01-settings.css)
CSS variables/design tokens. No selector.
Generic(02-generic.css)
Reset/normalize. Widespread influence on all elements.
Elements(03-elements.css)
Default style for plain HTML elements (h1 to h6, p, a, table, etc.).
Objects(04-objects.css)
Layout patterns (.container, .grid, .flex, etc.). No design.
Components(05-components.css)
UI components (.btn, .badge, .card, etc.). With design.
Utilities(06-utilities.css)
Utility classes (.hidden, .text-center, etc.). !important Available.
Module CSS (each module.css)
Module-specific styles. The most local and detailed.
css/04-objects.css — Example object definitions for container and grid
/* ===== Objects: Layout pattern =====
   It does not have a design (color/font). Define only the structure.
===================================================== */

/* container */
.container {
  width: 100%;
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: 20px;
}
@media (min-width: 768px)  { .container { padding-inline: 32px; } }
@media (min-width: 1280px) { .container { padding-inline: 40px; } }/* section */
.section {
  padding-block: var(--section-padding);
}
.section--sm { padding-block: calc(var(--section-padding) * 0.5); }
.section--lg { padding-block: calc(var(--section-padding) * 1.5); }/* Grid */
.grid { display: grid; gap: var(--gap, 24px); }
.grid-2 { grid-template-columns: repeat(2, 1fr); }
.grid-3 { grid-template-columns: repeat(3, 1fr); }
.grid-4 { grid-template-columns: repeat(4, 1fr); }

@media (max-width: 768px) {
  .grid-2, .grid-3, .grid-4 { grid-template-columns: 1fr; }
}
@media (min-width: 480px) and (max-width: 1024px) {
  .grid-3, .grid-4 { grid-template-columns: repeat(2, 1fr); }
}

Section 1-9

Upload your first theme

Once you're set up, upload your theme to HubSpot and see it working. Follow the steps below to complete the process from initial upload to confirmation.

Terminal — upload theme and launch watch
# ===== First time: Upload all files =====
$ hs upload src/my-theme @my-theme --portal=dev
Uploading "src/my-theme" to "@my-theme" on portal "dev"...
✔ Done! Uploaded 12 files.

# ===== Under development: Automatic synchronization in watch mode =====
$ hs watch src/my-theme @my-theme --portal=dev
Watcher started, watching "src/my-theme"
# Automatically uploads every time you save a file

# ===== Confirmation after upload =====
# HubSpot admin → Marketing → Design Manager
# Success if @my-theme is displayed in the left pane

# ===== Apply template to page and check =====
# Website page → Create new page
# → Select "page.html" as template
# → Check the display in preview
package.json — Manage development commands all at once
{
  "name": "my-company-hubspot",
  "scripts": {
    "dev"          : "hs cms watch src/my-theme @my-theme --portal=dev",
    "deploy:dev"   : "hs cms upload src/my-theme @my-theme --portal=dev --overwrite",
    "deploy:prod"  : "hs cms upload src/my-theme @my-theme --portal=prod --overwrite",
    "lint"         : "hs lint src/my-theme --portal=dev",
    "predeploy:prod": "npm run lint"
  },
  "devDependencies": {
    "@hubspot/cli": "^8.0.0" // CLI v8 or higher (Node 20 required)
  }
}

Section 1-10

Chapter 1 Summary

📌 Key points to keep in mind in this chapter

Essentials of HubSpot CMS

SaaS type CMS integrated with CRM/MA. No infrastructure management required, global CDN installed as standard. Design after understanding the constraints (HubL only/backend processing not possible).

Checking the plan is the starting point of design

HubDB, smart content, and multiple blogs are for Professional or higher. We will check the plan immediately after receiving your order, and if there are any features that cannot be used, we will suggest alternatives in advance.

3-piece development environment set

Developer Account (free) / Personal Access Key (scope setting required) / hubspot.config.yml (must be added to .gitignore). These three things will prepare you for development.

Directory design principles

Separation of templates / layouts / partials / modules / css / js is basic. Modules are managed in .module directory units (1 set of 5 files).

theme.json is the starting point for the design

Define color, font, and spacing in theme.json → Expand as CSS variables → Reference in all files. Hard codes are prohibited. Changes must be made through theme.json.

CSS is layered with ITCSS

Define in the order of Settings → Generic → Elements → Objects → Components → Utilities. Module-specific styles are separated into module.css. Preserving layers prevents level-of-detail collisions.

Next chapter: Chapter 2 HubL (HubSpot Markup Language) Basics

We will systematically explain the grammar, variables, filters, functions, and control syntax of HubSpot's unique template language HubL.

Go to Chapter 2 →