Skip to content

Lazy Loaded Form

HubSpot forms without the layout shift

Embed any HubSpot form while deferring its embed script until visitors actually need it. Lazy Loaded Form reserves space up front to eliminate layout shift (CLS) and keeps heavy form JavaScript off the critical path for a faster LCP — so your Core Web Vitals stay green without sacrificing conversions. Works with both new (2024+) and legacy forms, with load-on-scroll, load-on-click, or on-idle strategies.

Form Heading

Better Core Web Vitals, without losing the form

Standard HubSpot form embeds tend to load a heavy script on page load and pop the form in after paint — dragging down your LCP and triggering the layout shift that tanks your CLS score. Lazy Loaded Form flips that: it renders reserved space instantly and waits to fetch the form until a visitor actually needs it. You get a faster, more stable page that keeps Google happy — and a form that's ready the moment it matters.

Eliminate layout shift (CLS)

Reserved space is rendered up front, so surrounding content never jumps when the form appears.

Faster LCP

The ~200KB+ of HubSpot form JavaScript is deferred off the critical path, letting your main content paint first.

Load exactly when needed

Choose load-on-scroll, load-on-click, or on-idle so the form arrives at the right moment for each placement.

Smart preloading

On-scroll loading fetches the form just before it enters view, so any size change happens off-screen and never counts against CLS.

New and legacy forms

Works with both HubSpot's 2024+ form editor and classic forms, auto-detecting the right embed method.

Polished loading state

A built-in shimmer placeholder (with reduced-motion support) keeps the experience smooth while the form loads.

No layout guesswork

Style, size, and alignment controls plus per-instance form styling let it match any page without touching code.

A Little Bit About How it Works

Need This Module Customized for Your Brand or Function?

(or any other module for that matter?)

Let A Design Link tailor this event calendar to your exact specifications—from custom color schemes and unique layouts to special features and integrations. Our HubSpot experts can modify any aspect of this module or build completely custom functionality to match your vision.

Purchase a development package and get:
  • Expert HubSpot CMS customization
  • Brand-perfect styling and design tweaks
  • Custom feature development
  • Performance optimization
  • Ongoing support and maintenance

Perfect for module customization, theme development, and HubSpot CMS consulting.

Documentation

Lazy Loaded Form

A HubSpot CMS module that embeds a HubSpot form while deferring the forms embed script until the form is actually needed. It reserves the form's vertical space up front so the page never jumps when the form appears.

The result: better CLS (no layout shift), better LCP (the ~200KB+ of HubSpot form JavaScript is kept off the critical path), and less main-thread work on initial load.

  • Categories: Forms & Buttons, Functionality
  • Content types: Landing pages, site pages, blog posts, blog listing
  • Supports: New forms (2024+ editor) and Legacy / classic forms

Why this module exists

HubSpot's standard form embed injects js.hsforms.net and renders the form client-side immediately. Two problems follow:

  1. LCP — the browser downloads and executes a large form bundle as part of the initial load, competing with content paint.
  2. CLS — the form renders after first paint and pushes surrounding content down, producing a layout shift.

This module fixes both:

  • Layout is server-rendered with a reserved-height placeholder, so the browser paints instantly and the space for the form already exists.
  • The embed script is loaded on demand — on scroll, on interaction, or when the browser is idle. When "on scroll" preloading is used, the form is fetched before it enters the viewport, so any height change happens off-screen and is not counted toward CLS at all (CLS only measures shifts inside the viewport).

↑ Back to Quick Links


How it works (technical overview)

module.html  ->  Renders a reserved-space placeholder + data-* config.
                 Resolves the form ID / portal ID / region server-side.
module.css   ->  Namespaced (blk_lzf) base styles + skeleton shimmer.
module.js    ->  Reads data-* attributes, loads the correct embed script
                 on the chosen trigger, mounts the form, removes the
                 placeholder.

The template writes the form's configuration onto a container element as data attributes:

Attribute Purpose
data-form-id The HubSpot form ID (GUID or numeric)
data-portal-id HubSpot account/portal ID
data-region Data region (na1, na2, eu1)
data-embed-version new or legacy — selects the embed method
data-strategy viewport | interaction | idle | immediate
data-root-margin Preload distance in px for the viewport strategy

 

module.js then:

  • Legacy forms → loads js.hsforms.net/forms/embed/v2.js and calls hbspt.forms.create({ portalId, formId, region, target }). The onFormReady callback removes the placeholder.
  • New forms → loads js.hsforms.net/forms/embed/<portalId>.js (once per portal) and inserts a .hs-form-frame element with the data attributes. A MutationObserver (plus a timeout fallback) detects render and removes the placeholder.

Each embed script is fetched at most once per page and shared across every instance of the module.

↑ Back to Quick Links


Loading strategies

Set under Loading Behavior → When to load the form.

Strategy When the form loads Best for
On scroll Just before it scrolls into view (IntersectionObserver) Below-the-fold forms (default)
On interaction When the visitor clicks a "Load form" button Leanest initial load; low-intent placements
When idle After load, via requestIdleCallback Forms that should appear without a click but aren't urgent
Immediately As soon as the module script runs Above-the-fold / primary-CTA forms

 

In the HubSpot page editor the module always loads immediately so authors can see and configure the form regardless of the selected strategy.

Preload distance (On scroll only)

root_margin (default 200px) controls how far below the viewport loading begins. Larger = the form is ready earlier (before it's seen), which keeps its height change off-screen and out of your CLS score. Smaller = leaner but the form may still be assembling as it enters view.

↑ Back to Quick Links


Reserving space (the CLS control)

Under Loading Behavior:

  • Reserve space (default on) — reserves vertical space before the form loads.
  • Reserved height (default 400px) — set this close to your form's real rendered height.

Set the reserved height accurately. If it matches the form, there's no gap and no jump. If it's much smaller than the form and the form loads while on-screen, the container will grow and cause a shift. With "On scroll" preloading, growth typically happens off-screen and doesn't count — but a close value is still the safest configuration, especially for above-the-fold forms.

↑ Back to Quick Links


Choosing and configuring the form

The Form type field determines how you pick the form.

New form (2024+ editor) — default

New forms are not available in HubSpot's module form picker (a HubSpot platform limitation, not a module bug). Instead, paste the form's embed code:

  1. In HubSpot go to Marketing → Forms and open your form.
  2. Click Share → Embed.
  3. Copy the full embed snippet and paste it into the module's Form embed code field. It looks like:
<script src="https://js.hsforms.net/forms/embed/1234567.js"></script>
<div class="hs-form-frame" data-region="na1"
     data-form-id="0f1a2b3c-4d5e-..." data-portal-id="1234567"></div>

The module automatically extracts the form ID, portal ID, and region from the snippet. Pasting just the bare form ID also works — in that case the module falls back to the current account's portal ID and the region set under Advanced.

Legacy / classic form

Select Legacy / classic form to use HubSpot's native form picker dropdown. Portal ID comes from the account automatically; set the region under Advanced if your account is not na1.

↑ Back to Quick Links


Fields reference

Top level

Field Type Notes
Form type choice New form (2024+ editor) (default) or Legacy / classic form
Form embed code text Shown for New forms. Paste the Share → Embed snippet.
Form form Shown for Legacy forms. HubSpot's native form picker.

Content

Field Type Default Notes
Heading text (empty) Optional heading above the form.
Heading Tag choice h2 For correct document outline / SEO.
Accessible Label text Form ARIA label for the form region.
Load Button Text text Load form Used only by the "On interaction" strategy.

Loading Behavior

Field Type Default Notes
When to load choice viewport See Loading strategies.
Preload distance number 200 px Viewport strategy only.
Reserve space boolean true Toggles CLS-preventing space reservation.
Reserved height number 400 px Approximate rendered form height.

Advanced

Field Type Default Notes
Form region choice na1 Account data region: na1, na2, or eu1. Used for legacy forms and as a fallback for new forms. Overridden by data-region in a pasted embed code.

Styles (STYLE tab)

  • Layout — max width, alignment, container padding.
  • Colors — container background, heading color.
  • Loading Placeholder — shimmer on/off, base/shine colors, corner radius.
  • Load Button (interaction mode) — background, text color, corner radius.
  • Form Style Overrides — Legacy forms only (see below).

↑ Back to Quick Links


Form Style Overrides (Legacy forms only)

Because legacy forms render as inline HTML (not an iframe), their fields can be restyled with CSS. This group is hidden unless Form type = Legacy, and is gated behind a master "Override HubSpot form styles" toggle. When off, the form inherits your HubSpot/theme styling untouched.

Available controls:

  • Field spacing between fields.
  • Field Labels — color, font size, weight.
  • Inputs — text/background/border colors, focus border, border width, radius, padding, font size (defaults to 16px to prevent iOS zoom-on-focus).
  • Checkboxes & Radio Buttons — accent color (via CSS accent-color), option label color, control size.
  • Submit Button — background, hover, text color, font size, radius, padding, full-width toggle.
  • Validation — error message / error border color.

All override CSS is scoped to the module instance (#blk_lzf_frame_<name>) so it never affects other forms on the page. Each color is guarded — leaving a picker empty inherits the existing style instead of blanking it.

New forms cannot be restyled this way; style them in HubSpot's form editor.

↑ Back to Quick Links


Accessibility

  • The form container is a labelled region with aria-busy reflecting load state.
  • A visually hidden aria-live status announces "Loading form…", "Form loaded.", or an error.
  • The interaction button is a real <button> with a visible focus ring.
  • The skeleton shimmer respects prefers-reduced-motion.
  • A <noscript> message is shown if JavaScript is disabled.

↑ Back to Quick Links


Performance notes / best practices

  • Above-the-fold forms: use Immediately (or On scroll with a large preload distance) and set an accurate Reserved height.
  • Below-the-fold forms: On scroll (default) is ideal — the form is ready before it's seen and the layout change never counts against CLS.
  • Secondary / low-priority forms: On interaction avoids loading the form bundle entirely unless the visitor engages.
  • Multiple instances on one page are safe — unique IDs prevent collisions and the embed script is loaded only once per page (per portal for new forms).

↑ Back to Quick Links


Troubleshooting

Symptom Cause / fix
New form doesn't render Confirm Form type = New form and that the pasted embed code contains data-form-id.
Form loads but is unstyled by overrides Style Overrides only apply to Legacy forms. Style new forms in HubSpot.
Form appears blank on an EU account Ensure the region is correct — in the pasted embed code (data-region="eu1") or the Advanced → Form region field for legacy forms.
Content still shifts when the form loads Increase Reserved height to match the real form, and/or increase Preload distance.
hbspt is not defined in console Don't mix embed types. The Form type toggle handles this — make sure it matches your form.

↑ Back to Quick Links


Known limitations

  • No native new-form picker. HubSpot does not expose new (2024+) forms to any custom-module field, so new forms are configured by pasting their embed code. This is a documented platform limitation with an open feature request.
  • Field-level Style Overrides are legacy-only for the reason above.
  • New-form "ready" detection is best-effort. The new embed offers no reliable inline ready callback, so a MutationObserver + timeout fallback is used. The reserved height makes the exact removal moment non-critical for CLS.

↑ Back to Quick Links