Ever wondered where your leads actually come from? If you're not capturing UTM parameters and referrer data in your forms, you're flying blind.
TL;DR
Form Attribution is a lightweight (~5KB), zero-dependency JavaScript library that automatically captures marketing attribution data (UTM parameters, referrer URLs, ad click IDs) and injects them as hidden fields into your forms. One script tag. No configuration required.
<script src="https://cdn.jsdelivr.net/npm/form-attribution@latest/dist/script.min.js"></script>
GitHub Repository | Documentation | Interactive Builder
The Problem: Marketing Attribution Gap
Here's a scenario every marketer and developer has faced: A lead submits a form on your website. Sales closes the deal. But when leadership asks "What campaign brought them in?" — silence.
The attribution data was in the URL when the visitor landed. It was in the document.referrer. But by the time they filled out the form three pages later, that data was gone.
Common workarounds that don't scale:
- Manually adding hidden fields to every form
- Building custom JavaScript for each project
- Relying on CRM-specific tracking scripts
- Asking users "How did you hear about us?" (unreliable)
What is Form Attribution?
Form Attribution is an open-source JavaScript library that solves the attribution gap with minimal effort. It captures visitor data on first touch and persists it until form submission.
How Does Form Attribution Work?
- Capture: On page load, captures URL parameters and metadata
- Persist: Stores data in sessionStorage, localStorage, or cookies
- Inject: Automatically adds hidden fields to all forms on the page
- Observe: Uses MutationObserver to handle dynamically-added forms
What Parameters Does Form Attribution Capture?
URL Parameters (captured by default):
| Parameter | Description |
|---|---|
utm_source |
Traffic source (e.g., google, newsletter) |
utm_medium |
Marketing medium (e.g., cpc, email) |
utm_campaign |
Campaign name |
utm_term |
Paid search keywords |
utm_content |
Content variant for A/B testing |
utm_id |
Campaign ID |
ref |
Referrer tracking parameter |
Metadata (automatically captured):
| Parameter | Description |
|---|---|
landing_page |
First page URL visited |
current_page |
Current page URL (form submission page) |
referrer_url |
Document referrer |
first_touch_timestamp |
ISO 8601 timestamp of first visit |
Ad Platform Click IDs (optional):
| Parameter | Platform |
|---|---|
gclid |
Google Ads |
fbclid |
Meta Ads |
msclkid |
Microsoft Advertising |
ttclid |
TikTok Ads |
li_fat_id |
LinkedIn Ads |
twclid |
Twitter/X Ads |
Quick Start: Basic Implementation
Add this single line before your closing </body> tag:
<script src="https://cdn.jsdelivr.net/npm/form-attribution@latest/dist/script.min.js"></script>
That's it. The script will:
- Capture UTM parameters and metadata from the current session
- Store them in sessionStorage (default)
- Inject hidden fields into every form on the page
- Monitor for dynamically-added forms via MutationObserver
What Gets Injected Into Forms?
Each captured parameter becomes a hidden input:
<input type="hidden"
name="utm_source"
value="google"
data-form-attribution="true"
data-form-attribution-managed="true">
Key behaviors:
- Existing hidden fields with matching names are updated (no duplicates)
- User-visible form fields are never modified
- All values are HTML-entity encoded for XSS protection
Configuration Options
For more control, use data attributes on the script tag:
<script src="https://cdn.jsdelivr.net/npm/form-attribution@latest/dist/script.min.js"
data-storage="localStorage"
data-field-prefix="attr_"
data-extra-params="custom_param,another_param"
data-exclude-forms=".no-track, [data-no-attribution]"
data-click-ids="true"
data-debug="true">
</script>
Available Configuration Options
| Attribute | Default | Description |
|---|---|---|
data-storage |
sessionStorage |
Storage method: sessionStorage, localStorage, or cookie
|
data-field-prefix |
"" |
Prefix for hidden field names (e.g., attr_ → attr_utm_source) |
data-extra-params |
"" |
Comma-separated additional URL parameters to capture |
data-exclude-forms |
"" |
CSS selector for forms to exclude |
data-storage-key |
form_attribution_data |
Custom storage key name |
data-debug |
false |
Enable console logging and debug panel |
data-privacy |
true |
Set "false" to disable GPC/DNT detection |
data-click-ids |
false |
Set "true" to capture ad platform click IDs |
Storage Options Explained
When to Use sessionStorage (Default)
Best for single-session attribution where you want data cleared when the browser closes.
<script src="https://cdn.jsdelivr.net/npm/form-attribution@latest/dist/script.min.js"
data-storage="sessionStorage">
</script>
When to Use localStorage
Best for first-touch attribution across multiple sessions.
<script src="https://cdn.jsdelivr.net/npm/form-attribution@latest/dist/script.min.js"
data-storage="localStorage">
</script>
When to Use Cookies
Best for cross-subdomain tracking or server-side access.
<script src="https://cdn.jsdelivr.net/npm/form-attribution@latest/dist/script.min.js"
data-storage="cookie"
data-cookie-domain=".example.com"
data-cookie-expires="90"
data-cookie-samesite="lax">
</script>
Cookie-specific options:
| Attribute | Default | Description |
|---|---|---|
data-cookie-domain |
"" |
Cookie domain (e.g., .example.com) |
data-cookie-path |
/ |
Cookie path |
data-cookie-expires |
30 |
Expiration in days |
data-cookie-samesite |
lax |
SameSite policy: lax, strict, or none
|
Storage Fallback Chain
If your preferred storage isn't available, the script falls back gracefully:
| Requested | Fallback Chain |
|---|---|
localStorage |
localStorage → sessionStorage → cookie → memory |
sessionStorage |
sessionStorage → cookie → memory |
cookie |
cookie → memory |
JavaScript API for Custom Integrations
Form Attribution exposes a global FormAttribution object:
// Get all attribution data
const data = FormAttribution.getData();
// → { utm_source: 'google', utm_medium: 'cpc', ... }
// Get a specific parameter
const source = FormAttribution.getParam('utm_source');
// → 'google'
// Get all tracked forms with status
const forms = FormAttribution.getForms();
// Clear stored data
FormAttribution.clear();
// Re-inject data into forms
FormAttribution.refresh();
Event Callbacks
// Fires when initialization is complete
FormAttribution.on('onReady', ({ data, config }) => {
console.log('Attribution ready:', data);
});
// Fires when new data is captured
FormAttribution.on('onCapture', ({ data }) => {
console.log('New data captured:', data);
});
// Fires when data is updated
FormAttribution.on('onUpdate', ({ data }) => {
console.log('Data updated:', data);
});
// Remove a callback
FormAttribution.off('onCapture', myHandler);
Privacy-First Design
By default, Form Attribution respects user privacy preferences:
-
Global Privacy Control (GPC): Tracking disabled when
navigator.globalPrivacyControlis true - Do Not Track (DNT): Tracking disabled when DNT is enabled
When privacy signals are detected, no data is captured or stored.
<!-- Override privacy detection (not recommended) -->
<script src="https://cdn.jsdelivr.net/npm/form-attribution@latest/dist/script.min.js"
data-privacy="false">
</script>
Debug Panel for Development
Enable the visual debug panel during development:
<script src="https://cdn.jsdelivr.net/npm/form-attribution@latest/dist/script.min.js"
data-debug="true">
</script>
Debug panel features:
- Data Tab: View all captured UTM parameters and metadata
- Forms Tab: See all forms and their injection status (click to highlight)
- Log Tab: Real-time activity log with timestamps
- Actions: Copy data as JSON, clear storage, refresh forms
The panel is draggable, collapsible, and uses Shadow DOM for style isolation.
⚠️ Remove
data-debugbefore deploying to production.
Real-World Use Cases
Use Case 1: HubSpot Form Integration
Capture UTM parameters and pass them to HubSpot for closed-loop reporting:
<script src="https://cdn.jsdelivr.net/npm/form-attribution@latest/dist/script.min.js"
data-field-prefix="hs_"
data-storage="localStorage">
</script>
Use Case 2: Multi-Domain Tracking
Track attribution across subdomains (blog.example.com → app.example.com):
<script src="https://cdn.jsdelivr.net/npm/form-attribution@latest/dist/script.min.js"
data-storage="cookie"
data-cookie-domain=".example.com"
data-cookie-expires="30">
</script>
Use Case 3: Paid Ad Campaign Tracking
Capture all major ad platform click IDs for offline conversion tracking:
<script src="https://cdn.jsdelivr.net/npm/form-attribution@latest/dist/script.min.js"
data-click-ids="true"
data-storage="localStorage">
</script>
Use Case 4: Exclude Login/Auth Forms
Prevent attribution injection on sensitive forms:
<script src="https://cdn.jsdelivr.net/npm/form-attribution@latest/dist/script.min.js"
data-exclude-forms=".login-form, .auth-form, [data-no-track]">
</script>
<form class="login-form">
<!-- No attribution fields injected here -->
</form>
Why Not Just Use Google Analytics?
Great question. Google Analytics is excellent for aggregate traffic analysis. But it doesn't help when you need lead-level attribution in your CRM, email platform, or database.
Form Attribution complements GA by:
- Passing attribution data directly into form submissions
- Enabling CRM-native attribution reporting
- Supporting offline conversion tracking
- Working without cookies (sessionStorage/localStorage options)
FAQ
Does Form Attribution work with React/Vue/Angular?
Yes. The MutationObserver monitors the entire DOM for form elements, including those rendered by JavaScript frameworks.
Does it work with iframes?
The script only operates on forms within its own document context. For cross-origin iframes, you'll need to include the script in each iframe.
What about GDPR/CCPA compliance?
Form Attribution respects GPC and DNT by default. For full compliance, ensure your privacy policy discloses the use of attribution tracking and provide users a mechanism to opt-out.
Can I use it with Webflow/WordPress/Squarespace?
Yes. Add the script via your platform's custom code injection feature (usually in site-wide footer scripts).
Performance & Bundle Size
- Zero dependencies: No jQuery, no React, no external libraries
- ~5KB minified: Minimal impact on page load
- Lazy initialization: Runs after DOMContentLoaded
- Efficient DOM updates: Uses MutationObserver, not polling
Get Started
Add a single script tag before your closing </body> tag:
<script src="https://cdn.jsdelivr.net/npm/form-attribution@latest/dist/script.min.js"></script>
That's it — no build step, no package manager, no configuration required.
Interactive Script Builder
Not sure which options you need? Use the Interactive Script Builder to generate a configured script tag with a visual interface.
Contributing
Form Attribution is open source under the Apache-2.0 license. Contributions welcome!
Conclusion
Marketing attribution shouldn't require complex integrations or heavyweight tracking platforms. Form Attribution provides a simple, privacy-respecting solution that works out of the box with any form on any website.
One script tag. Zero configuration. Complete attribution data.
Built by Ben Sabic at Flash Brew Digital
Top comments (0)