P
ProFormsDocs

JavaScript Embed

The recommended way to embed ProForms on any website. Add a single script tag and the form renders itself โ€” full styling, validation, auto-formatting, and submission handling included. No dependencies, no build step.

Basic Embed

Drop this snippet wherever you want the form to appear:

HTML
<script src="https://proforms.io/embed.js" data-form="YOUR_FORM_UUID"></script>

The script inserts the form immediately after itself. Copy the snippet from your form's Share tab in the builder.

Embed Modes

Control how the form's CSS interacts with your site using data-mode:

HTML
<!-- Isolated (default) โ€” Shadow DOM, fully scoped, no CSS bleed -->
<script src="https://proforms.io/embed.js"
        data-form="YOUR_FORM_UUID"
        data-mode="isolated"></script>

<!-- Open โ€” inherits your site&apos;s CSS, useful for tight design integration -->
<script src="https://proforms.io/embed.js"
        data-form="YOUR_FORM_UUID"
        data-mode="open"></script>
AttributeValuesDescription
data-formstring (required)Your form UUID
data-mode"isolated" | "open"Shadow DOM isolation (default: "isolated")

Caching & Updates

The embed fetches your form config from /api/v1/f/:uuid on every page load. Responses use Cache-Control: no-cache with an ETag based on the form version, so the browser revalidates on every load but only downloads new data when the form has changed (304 Not Modified otherwise).

๐Ÿ’ก
Changes made via the API or dashboard are reflected on the next page load โ€” no cache purging needed. The form's version number increments on every update and drives cache invalidation automatically.

Multiple Forms on One Page

Add multiple script tags โ€” each renders independently:

HTML
<!-- Contact form -->
<script src="https://proforms.io/embed.js" data-form="FORM_UUID_1"></script>

<!-- Newsletter signup -->
<script src="https://proforms.io/embed.js" data-form="FORM_UUID_2"></script>

reCAPTCHA v3

When settings.security.recaptchaEnabled is true on a form, the embed automatically loads the reCAPTCHA v3 script and attaches a token to every submission. No extra configuration needed.

The embed resolves the reCAPTCHA site key using this priority order:

  1. The site's custom recaptchaSiteKey (if configured via the Sites API or Site Settings)
  2. The ProForms platform global key (fallback โ€” works on any domain)

Server-side verification uses the matching secret key in the same priority order. The score threshold is controlled per-form via settings.security.recaptchaThreshold (default: 0.5).

Badge Display

Google requires either the floating reCAPTCHA badge or a text attribution to be visible. Control this per-form with settings.security.recaptchaBadgeMode:

ValueBehavior
"text"Default. Hides the floating badge. Shows a centered one-line attribution at the bottom: "Protected by reCAPTCHA ยท Privacy ยท Terms"
"badge"Shows Google's default floating corner badge. No text attribution added.
"none"Hides both badge and text. You're responsible for adding your own reCAPTCHA attribution.
๐Ÿ’ก
If you're using the platform global key, make sure domain verification is disabled in your Google reCAPTCHA admin console so the key works on any domain your forms are embedded on.

Google Ads Tracking

ProForms keeps tracking simple with two site modes: Enhanced + Tag for browser-side conversions after successful submit, and Disabled for offline-feed-only or client-owned thank-you page tracking.

How It Works

  1. Enhanced + Tag: the embed loads configured tags and fires Google Ads conversion tracking only after successful submit.
  2. If Enhanced Conversions are enabled, ProForms hashes supported user fields and sends them with the conversion event.
  3. The Google Ads conversion event includes a transaction ID from the submission so browser and offline paths can be reconciled.
  4. Disabled: the embed does not load pixels or emit browser tracking. Use the offline conversion feed or a client-owned thank-you page snippet.

Setup via API

cURL
curl -X PUT https://proforms.io/api/v1/forms/:id \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "settings": {
      "tracking": {
        "googleAdsId": "AW-123456789",
        "googleAdsConversionLabel": "AbCdEfGhIjKlMn"
      }
    }
  }'

Enhanced + Tag Enhanced Conversions

Enhanced + Tag mode maps form field types to Google's user_data schema when enhanced conversions are enabled:

Field TypeGoogle user_data KeyProcessing
emailsha256_email_addressLowercased, trimmed, SHA-256 hashed
phonesha256_phone_numberNon-digit characters stripped (except +), SHA-256 hashed
nameaddress.sha256_first_name / sha256_last_nameSplit name auto-detected; full name split on first space. SHA-256 hashed.
addressaddress.street / city / region / postal_code / countryPassed as plaintext (Google's spec for address fields)

Compatible Field Types

Enhanced Conversions detection is based on the field typeyou choose in the form builder โ€” not the field label, key, or ID. Renaming a field's key or label has no effect on detection. As long as the field type is one of the four above, it works.

Will Work โœ…Won't Work โŒ
An Email field (type: email) with key renamed to my_emailA Text field (type: text) labeled "Email Address"
A Phone field (type: phone) with any custom labelA Number field (type: number) labeled "Phone"
A Name field (type: name) in either full or split formatTwo separate Text fields for first/last name
An Address field (type: address) with all sub-fieldsIndividual Text fields for street, city, etc.
โš ๏ธ
Use one primary conversion path per form. If Enhanced + Tag is primary, keep the offline feed secondary/backup unless you intentionally dedupe both paths.

Important Notes

  • Privacy-first: All PII (email, phone, name) is SHA-256 hashed in the browser before being sent to Google. Raw values are never transmitted to Google's servers.
  • Minimum data: At least one email or phone field is required for enhanced matching. If neither exists, a standard conversion fires instead.
  • Multiple fields: If your form has multiple email or phone fields, the last one in the form is used.
  • Hidden fields: Fields hidden by conditional logic at the time of submission are excluded.
  • Google Ads requirements: You must enable Enhanced Conversions in your Google Ads account (Settings โ†’ Conversions โ†’ Enhanced Conversions) for the data to be used.
  • Both IDs required: You need both the Google Ads ID (AW-XXXXXXXXX) and the Conversion Label from your conversion action. Without the label, no conversion event fires.
๐Ÿ’ก
Disabled mode is best when the offline conversion feed is the primary Google Ads source or the client tracks a separate thank-you page.

dataLayer / Custom Event

In Enhanced + Tag mode, ProForms also pushes a clean proforms_submission_success event after successful submission for additional analytics/GTM workflows:

dataLayer Event
{
  event: "proforms_submission_success",
  proforms_form_id: "uuid",
  proforms_form_name: "Contact Form",
  proforms_site_name: "Client Site",
  proforms_reference_id: "ABC-123",
  proforms_submission_id: "sub_123"
}

Create a GTM Custom Event trigger for proforms_submission_success if you need additional site-owned analytics or non-primary tracking.

What the Embed Handles

  • Field rendering โ€” all field types including file upload
  • Client-side validation with error messages
  • Email field format validation (standard, strict, or none)
  • Phone field auto-formatting and digit-only enforcement
  • Conditional logic (show/hide fields based on values)
  • Theme CSS variables from your form settings
  • Custom CSS injection (scoped to shadow DOM in isolated mode)
  • reCAPTCHA v3 with hybrid key resolution and honeypot spam protection
  • Enhanced + Tag success events for analytics/GTM workflows
  • Google Analytics, Facebook Pixel, and Google Ads tracking only in Enhanced + Tag mode
  • UTM parameter capture
  • File upload with progress
  • Multi-step form navigation
  • Thank you message or redirect after submission
๐Ÿ’ก
Use isolated mode (default) for most sites โ€” it prevents CSS conflicts and ensures your form looks exactly as designed. Switch to openmode if you need the form to inherit your site's font family or want to style it entirely with your own CSS.

CSS Class Reference

Target these classes in your customCss to style form elements. Short .pf-* aliases are recommended. Legacy .proform-* names also work.

Structure & Layout

ClassLegacyElement
.pf-wrapper.proform-wrapperOutermost container (centers the form, applies max-width)
.pf-form.proformThe <form> element
.pf-header.proform-headerForm header area (title + description)
.pf-body.proform-bodyField grid container โ€” CSS Grid with 6 columns. Controls gap between all fields.
.pf-field.proform-fieldIndividual field wrapper (default: spans all 6 columns = full width)

Field Width Modifiers

Applied alongside .pf-field to control column span in the 6-column grid. On screens โ‰ค480px, all fields collapse to full width.

ClassGrid SpanWidth
.pf-width-halfspan 3 of 650% โ€” two fields side by side
.pf-width-thirdspan 2 of 633% โ€” three fields in a row
(no modifier)span 6 of 6100% โ€” full width (default)

Form Fields

ClassLegacyElement
.pf-label.proform-labelField label
.pf-required.proform-requiredRequired asterisk (*)
.pf-help.proform-helpHelp text below a field
.pf-error.proform-errorValidation error message
.pf-input.proform-inputText, number, email, phone, date, time inputs
.pf-textarea.proform-textareaTextarea
.pf-select.proform-selectSelect dropdown
.pf-file.proform-fileFile upload input
.pf-file-wrapper.proform-file-wrapperFile upload container (drag zone + button)

Choice Fields

ClassLegacyElement
.pf-option.proform-optionIndividual radio/checkbox option row
.pf-options-grid.proform-options-gridOptions grid container (radio/checkbox layout)
.pf-card-option.proform-card-optionCard-style option (bordered)
.pf-cols-1โ€”Options grid: 1 column
.pf-cols-2โ€”Options grid: 2 columns
.pf-cols-3โ€”Options grid: 3 columns
.pf-cols-4โ€”Options grid: 4 columns

Compound Fields

ClassLegacyElement
.pf-name-split.proform-name-splitSplit name field container (First + Last side by side)
.pf-address-group.proform-address-groupAddress field group wrapper
.pf-address-row.proform-address-rowAddress sub-row (City+State, Zip+Country)
.pf-address-sublabel.proform-address-sublabelSmall label below each address input

Content & UI

ClassLegacyElement
.pf-heading.proform-headingHeading element
.pf-paragraph.proform-paragraphParagraph text
.pf-divider.proform-dividerHorizontal divider
.pf-disclaimer.proform-disclaimerDisclaimer text above submit button
.pf-submit.proform-submitSubmit button
.pf-success.proform-successSuccess/thank-you message container
.pf-success-icon.proform-success-iconSuccess checkmark icon
.pf-recaptcha-attr.proform-recaptcha-attrreCAPTCHA text attribution

State Classes

ClassApplied ToWhen
.pf-error.pf-fieldField has a validation error (also added as .proform-error-state)
.pf-selected.pf-card-optionCard option is selected
.pf-hidden.pf-fieldField hidden by conditional logic
.pf-loading.pf-formForm is submitting (shows spinner)

CSS Variables

These CSS custom properties are set automatically from your form's theme settings. Override them in customCss for full control.

VariableDefaultControls
--pf-bg#ffffffForm background color
--pf-text#111827Text color
--pf-border#d1d5dbInput border color
--pf-border-width1pxInput border width
--pf-error#ef4444Error text & border color
--pf-primary#3b82f6Primary/accent color (checkboxes, radio fills)
--pf-success#10b981Success state color
--pf-btn-color#111827Submit button background
--pf-btn-text#ffffffSubmit button text color
--pf-btn-radius8pxSubmit button border-radius
--pf-radius8pxInput border-radius
--pf-fontsystem-uiFont family
--pf-font-size15pxBase font size
--pf-label-font-size15pxLabel font size
--pf-label-weight500Label font weight
--pf-heading-font-size24pxHeading element font size
--pf-input-height44pxInput minimum height
--pf-form-max-width600pxMaximum form width
--pf-box-shadownoneForm container box shadow
--pf-padding12pxInput inner padding
--pf-field-spacing18pxGap between fields (row-gap and column-gap)
--pf-form-padding20pxInner padding of the form body

Layout: Side-by-Side Fields

The form body (.pf-body) is a CSS Grid with 6 columns. Fields set to "Half Width" in the builder get .pf-width-half (span 3), creating side-by-side layouts. The gap between all fields โ€” including between side-by-side columns โ€” is controlled by --pf-field-spacing.

โš ๏ธ
In open embed mode, your site's CSS can override the grid gap. If side-by-side fields appear with no spacing, add this to your form's Custom CSS: .pf-body { gap: 16px !important; }
Example: Custom CSS
/* Adjust spacing between all fields */
.pf-body {
  gap: 24px;
}

/* Style inputs */
.pf-input, .pf-textarea, .pf-select {
  border-radius: 12px;
}

/* Style the submit button */
.pf-submit {
  background: #1a1a1a;
  border-radius: 12px;
}

/* Uppercase labels */
.pf-label {
  font-weight: 500;
  text-transform: uppercase;
  font-size: 11px;
  letter-spacing: 0.05em;
}

/* Override CSS variables */
.pf-form {
  --pf-field-spacing: 24px;
  --pf-radius: 12px;
  --pf-btn-color: #1a1a1a;
}