HubSpot CMS for Developers cheat sheet
HubSpot CMS for Developers · Keep the key ideas within reach.
HubSpot CMS for Developers
HubSpot CMS separates content (CMS Hub content, blog posts, pages) from presentation via HubL templates and modules; a page's layout resolves through the template assigned to it, then its modules, then per-instance field values.
Template types: page templates (single or drag-and-drop), blog templates (listing and post), system templates (404, password, search, email subscription), and partials included via {% include %} or {% module %}.
HubL syntax uses {% %} for logic tags and {{ }} for output; filters apply with the pipe ({{ content.post_list_summary | truncate(200) }}); comments use {# #}.
Local development uses the HubSpot CLI: hs init authenticates via personal access key, hs create scaffolds templates/modules, hs watch uploads changes, hs fetch pulls remote portal files.
Modules are reusable field groups defined in meta.json (fields, defaults, labels) plus module.html, module.css, module.js; local modules live in the theme and cannot be edited in the design manager UI.
Theme structure: theme.json (config, label, screenshots), templates/ and modules/ folders, css/ and js/ assets; themes are the deployable unit for marketplace distribution.
Drag-and-drop areas use {% dnd_area "main" %} with {% dnd_section %} and {% dnd_module path=... %} children; classic areas use {% widget_block %} or single-widget tags.
Serverless functions live in .functions/ with an exports.main = async (context) => {} signature and are served under /_hcms/api/; secrets are set in the portal, not committed to code.
hubl_render and content access: page data is exposed through the content object (content.title, content.body, content.absolute_url, content.featured_image) and blog data through the blog_* variables.
HubSpot CMS for Developers - Field, API, and Performance Rules
Module field types include text, richtext, image, link, boolean, choice, color, date, datetime, file, form, hubdbrow, menu, number, embed, and cta; repeating fields use the group field with occurrence settings.
Field values are read in templates with module.<field_name> for local modules and widget_data.<field_name> in drag-and-drop contexts; defaults from meta.json apply when no value is set.
HubDB tables are queried with hubdb_table_rows(table_id, query) and hubdb_table_row; filters accept column=value syntax and ordering via orderBy; dynamic pages bind a table plus a route.
The CMS Content API (api.hubapi.com/cms/v3/pages, /blog-posts, /site-pages) supports CRUD on content; the CMS Pages API publishes with a draft/publish workflow, and the Blog API manages posts and authors.
The HubSpot Forms API endpoint POST https://api.hsforms.com/submissions/v3/integration/submit/{portalId}/{formGuid} accepts fields, context, and legalConsentOptions; submissions require a valid form GUID and field names.
Contact and CRM data in templates: use the personalization tags ({% if contact %}, {{ contact.firstname }}) only on pages rendered to known contacts; anonymous visitors fall back to defaults.
Performance: HubL renders server-side, so avoid loops over large HubDB tables in listing templates; prefer hubdb_table_rows with limit and cached queries, and lazy-load images with loading="lazy".
Security: never expose API keys in module.js or templates; serverless functions read process.env secrets, and forms should use HubSpot's built-in spam protection or reCAPTCHA.
SEO and routing: canonical URLs come from content.canonical_url, sitemaps are auto-generated, and redirects are managed in Settings > Redirects or via the URL redirects API.