DEV Community

Cover image for Stop Losing Marketing Attribution Data in Your Forms (Zero-Dependency Solution)
Ben Sabic
Ben Sabic

Posted on

Stop Losing Marketing Attribution Data in Your Forms (Zero-Dependency Solution)

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>
Enter fullscreen mode Exit fullscreen mode

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?

  1. Capture: On page load, captures URL parameters and metadata
  2. Persist: Stores data in sessionStorage, localStorage, or cookies
  3. Inject: Automatically adds hidden fields to all forms on the page
  4. 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>
Enter fullscreen mode Exit fullscreen mode

That's it. The script will:

  1. Capture UTM parameters and metadata from the current session
  2. Store them in sessionStorage (default)
  3. Inject hidden fields into every form on the page
  4. 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">
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

Privacy-First Design

By default, Form Attribution respects user privacy preferences:

  • Global Privacy Control (GPC): Tracking disabled when navigator.globalPrivacyControl is 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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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-debug before 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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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)