---
title: "free HubL formatter and linter for HubSpot CMS - codefmt"
url: https://codefmt.dev/hubl
updated: 2026-08-24
sources:
  - https://codefmt.dev/hubl
licence: "© codefmt. Cite with attribution to https://codefmt.dev."
---

# free HubL formatter and linter for HubSpot CMS

codefmt formats and lints HubL (HubSpot's jinja2-based templating language) in the browser, with no HubSpot account, no portal connection, and no local theme checkout. paste a template, module, or email and get formatted output plus lint diagnostics with one-click safe fixes.

## the HubL linter

- 28 single-file rules across 6 categories: correctness, semantic, inheritance, deprecation, module shape, and hints
- 7 rules ship one-click safe auto-fixes (js-mindset rewrites, deprecated tag renames, and more)
- catches unclosed blocks, mismatched tags, unknown filter names, unknown globals with did-you-mean suggestions, deprecated tags, and js-mindset traps (|| instead of or, === instead of ==, null instead of none)
- full rule reference: https://codefmt.dev/hubl-lint-rules

## the whole-theme audit

drop a theme folder (or the .zip Design Manager exports) onto the page and every file is read, linted, and reported in the browser: 30 rules in whole-theme mode, including cross-file checks like extends/include path resolution that need the project graph. nothing is uploaded; the audit runs entirely client-side.

## the formatter

- normalizes delimiters inside {% %} and {{ }}, indents block-level tags and dnd hierarchies, and splits inline HTML onto properly nested lines
- preserves whitespace-control markers ({%- and -%}) and raw block content exactly
- auto-detects plain HTML or CSS (module.html or module.css without HubL tags) and formats it with Biome

## frequently asked questions

### what is HubL?

HubL (HubSpot Markup Language) is a jinja2-based templating language used in HubSpot CMS for building website pages, blog templates, email templates, landing pages, and custom modules. it extends jinja2 with HubSpot-specific tags like dnd_area, dnd_section, dnd_module, and functions for accessing hubdb, blog content, and crm data.

### what does the HubL linter catch?

28 rules across 6 categories. correctness: unclosed blocks (a for loop missing endfor), mismatched opening and closing tags, unexpected closers, elif after else. semantic: unknown filter names, wrong filter argument counts, unknown global identifiers with did-you-mean suggestions, unknown function names, and js-mindset traps that look right but break in HubL (|| should be or, && should be and, === should be ==, null should be none, .forEach should be a for tag, backtick template literals are not HubL). inheritance: duplicate block names, missing endblock, super() called outside a block. deprecation: widget should be module, widget_block should be module_block. module shape: missing path, unknown field types, malformed parameters (the 'form_id 7a3' pattern where = was forgotten). hints: nested raw blocks, empty blocks, |safe without preceding |escape, large range loops above 1000 iterations. 7 of these ship one-click safe auto-fixes via the Fix and Fix (unsafe) buttons.

### are there other HubL linters?

yes. HubSpot's official VS Code extension lints HubL inline (public beta) when a portal is connected via hubspot.config.yml, on theme files checked out locally. generic jinja2 template linters like djlint can lint HubL files as jinja, but they don't recognize HubSpot's tags, the filter catalog, or the js-mindset traps that make HubL distinct. prettier-plugin-hubl from HubSpot and the studionope.com HubL Code Formatter extension are formatters only. codefmt lints with HubL-specific rules in the browser, needs no HubSpot account or local setup, and pairs diagnostics with one-click fixes.

### what does the HubL formatter handle?

the formatter handles delimiter normalization (consistent spacing inside {% %} and {{ }}), block-level tag indentation, html tag splitting and indentation (inline html tags are separated onto their own lines and properly nested), single-line block collapse for short expressions, blank line insertion between top-level blocks, whitespace control marker preservation ({%- and -%}), and quote normalization across your template.

### can I format plain html or css from HubSpot modules?

yes. the formatter auto-detects whether your code is HubL, html, or css. if you paste plain html (like a module.html file without HubL tags) or css (like module.css), codefmt automatically formats it using Biome, a high-performance code formatter. HubL templates with embedded html are formatted by the HubL formatter, which splits inline html tags onto separate lines and indents them based on nesting.

### what filters are available in HubL?

HubL supports standard jinja2 filters like |title, |lower, |upper, |truncatewords, plus HubSpot-specific filters like |format_datetime, |format_currency_value, and |sanitize_html. the formatter preserves all filter expressions and their chaining while normalizing the surrounding whitespace. the linter also validates filter names against a curated 102-filter catalog and flags unknown filters (likely typos like |datetimeformatt instead of |datetimeformat) plus wrong filter argument counts.

### how does the formatter handle dnd tags and modules?

dnd_area, dnd_section, dnd_column, dnd_row, and dnd_module tags are treated as block-level elements and properly indented with their nested content. the formatter understands the drag-and-drop hierarchy and ensures each level is correctly indented, making complex dnd layouts much easier to read and maintain.

### does the formatter work with HubL macros and raw blocks?

yes. macro definitions and their contents are properly indented as block-level structures, including any nested control flow inside them. content inside {% raw %} blocks is preserved exactly as-is. the formatter will not modify anything between raw and endraw tags, which is important for javascript snippets embedded in templates.

### how does the formatter handle whitespace control markers like {%- and -%}?

the formatter fully supports HubL whitespace control markers. tags using {%- -%} or {{- -}} syntax are recognized and preserved during formatting. delimiter normalization still applies inside the markers, so you get consistent spacing while retaining your whitespace trimming behavior.

### what does the "docs-verified" date on the HubL formatter mean?

it is the date the linter's catalogs were last checked against HubSpot's own documentation. once a week a job re-reads HubSpot's published reference pages for tags, filters, functions, globals, and expression tests, plus the deprecations list, then diffs them against the catalogs this linter actually runs on. the date is when that check last came back clean, so it means the catalogs matched HubSpot's docs that day. if the diff turns up a change, say a filter HubSpot just added that would make the linter flag your working code as unknown, the date goes away until the catalogs are updated to match. it goes dark rather than going stale. a failed run, a gap waiting to be closed, or a last run more than 14 days old all drop the date entirely. so no date means the check is behind, not that anything is wrong with your template.

### can I format HubSpot email templates and blog templates?

yes. templates are formatted the same regardless of type. email-specific tags like unsubscribe_link and email_simple_subscription are recognized and put on their own lines, and blog loops format like any other for loop.

## for agents and automation

- format API: POST https://codefmt.dev/api/format (OpenAPI: https://codefmt.dev/api/openapi.json, 50KB max code, no authentication)
- MCP server: POST https://codefmt.dev/api/mcp (tools: format_code, format_json, ask_codefmt)
- site index for agents: https://codefmt.dev/llms.txt
- scoped context file: https://codefmt.dev/hubl/llms.txt
