# Hubspot form

External component: fetches a HubSpot form definition via the Marketing Forms API and renders a native, styled form on the site. Submissions go to HubSpot via the public submissions API.

> **Server token required.** The component shows an error indicator until `HUBSPOT_PAT` is configured. See [Required environment variables](#required-environment-variables).

## Fields & Schema

| Field | Type | Required | Notes |
|-------|------|----------|-------|
| `externalComponentType` | Symbol | Yes | Must be `"Hubspot form"` |
| `heading` | Symbol | No | Main title above the form. Renders as the section heading (`h1` sizing). |
| `textColour` | Symbol | No | Heading/body colour from the palette. Default: heading **Primary Blue**, body **Dark**. Set `Primary Blue` explicitly to match other page components. |
| `preHeading` | Symbol | No | Eyebrow line above the heading (`h6`). |
| `postHeading` | Symbol | No | Subtitle below the heading (`p1`). |
| `copy` | Rich Text | No | Introductory body copy above the form. |
| `extraCopy` | Rich Text | No | Shown in place of the form after a successful submission. When omitted, HubSpot’s thank-you message or redirect applies. |
| `anchor` | Symbol | No | HTML id for in-page links (e.g. `#form`). |
| `data.formId` | string | Yes | HubSpot form GUID from the form editor URL. |
| `data.portalId` | string | No | Overrides `HUBSPOT_PORTAL_ID` when set. |
| `data.submitButtonText` | string | No | Submit button label. Defaults to the HubSpot form label. |
| `data.widthPercentage` | number | No | Form width on laptop+ (1–100). Centered when set. |
| `data.hiddenFields` | object | No | Hidden field name → value map passed on submit. Overrides URL/session UTM auto-fill when set. |
| `data.initialValues` | object | No | Pre-filled visible field values. |

## UTM / campaign auto-fill

Hidden HubSpot form fields whose **internal names** match query params (e.g. `utm_source`, `utm_medium`, `utm_campaign`, `utm_term`, `utm_content`) are filled automatically. No CMS `hiddenFields` JSON is required for standard UTMs.

**How values are chosen (highest wins):**

1. HubSpot form definition defaults (if any)
2. Marketing params:
   - **First-touch session:** query params seen earlier in the visit are stored in `sessionStorage` (including on soft navigation) and used when the form page has no matching key
   - **Last-touch URL:** keys present on the **current form page** URL override the session for those keys
3. CMS `data.hiddenFields` / `data.initialValues` (static editor values win over URL/session)

Only **hidden** fields are auto-filled; visible fields are never overwritten by query params.

## Finding your form ID

From the HubSpot form editor URL:

`https://app.hubspot.com/forms/{portalId}/new-editor/{formId}`

Example: portal `51559001`, form `569f9ff5-f1d4-43d5-8bd1-875ebb2aa39f`.

## Usage

```bash
cms-edit add "Hubspot form" --content-type externalComponent --target content

cms-edit set @ref externalComponentType "Hubspot form"
cms-edit set @ref heading "Register for the webinar"
cms-edit set @ref textColour "Primary Blue"
cms-edit set @ref anchor "form"

cms-edit set @ref data --json '{
  "formId": "569f9ff5-f1d4-43d5-8bd1-875ebb2aa39f",
  "widthPercentage": 60,
  "submitButtonText": "Register"
}'

cms-edit set @ref extraCopy --rich-text "Thank you for registering. We will send joining details by email."

cms-edit save
```

## Required environment variables

| Variable | Where | Purpose |
|----------|-------|---------|
| `HUBSPOT_PORTAL_ID` | Vercel + `.env.local` | Portal ID for submissions (and default portal when `data.portalId` is omitted). |
| `HUBSPOT_PAT` | Vercel + `.env.local` (server only) | HubSpot **Service Key** with `forms` scope — fetches form field definitions. |

Submissions do **not** use `HUBSPOT_PAT`; only the server-side definition fetch does.

## Troubleshooting

| Symptom | Fix |
|---------|-----|
| Red error box: missing `HUBSPOT_PAT` | Add Service Key to Vercel env for that environment. |
| Red error box: failed to load form | Check form GUID, `forms` scope, and that the form exists in the same portal. |
| Heading not visible | Ensure `heading` is set; scroll the section into view (headings animate on `data-seen`). |
| Wrong heading colour | Set `textColour` to `Primary Blue`, or leave unset (defaults to primary blue). |
| Success message from HubSpot instead of CMS | Add content to `extraCopy`; when set, the form is replaced after submit. |