
Most websites that use Gravity Forms track UTM parameters in Google Analytics only. That gets you generic traffic reports. It doesn’t get you lead-level attribution.
When a lead submits your form, the entry brings in their name, email etc etc and your analytics says “47 sessions from a LinkedIn campaign this week.” Neither one can tell you which lead came from which campaign.
This snippet bridges that gap. It captures UTM parameters and Google Ads click IDs on arrival, holds them across pages, and writes them into hidden Gravity Forms fields at submission. Every entry ends up with the source data attached to the lead itself.
When to use this
Reach for this snippet if:
- Visitors often land on a blog post or resource page before reaching your contact form
- You run ads where the landing page and the form page aren’t the same URL
- You want UTM and click ID data inside Gravity Forms entries without installing a plugin
If visitors always land directly on your form page, Gravity Forms’ built-in Dynamic Population is enough on its own. Skip this.
The snippet
// UTM + Google Ads click ID capture for Gravity Forms.
// Captures values from any page a visitor lands on, persists them across
// the session, and populates matching hidden fields when the form loads.
//
// Replace 'myclass' and 'myitem' with your own handle
(function () {
var fieldMap = {
'utm_source': '.myclass-utm-source input',
'utm_medium': '.myclass-utm-medium input',
'utm_campaign': '.myclass-utm-campaign input',
'utm_content': '.myclass-utm-content input',
'utm_term': '.myclass-utm-term input',
'gclid': '.myclass-gclid input',
'gbraid': '.myclass-gbraid input',
'wbraid': '.myclass-wbraid input'
};
// Capture any tracked params present in the current URL
var params = new URLSearchParams(window.location.search);
Object.keys(fieldMap).forEach(function (key) {
var value = params.get(key);
if (value) sessionStorage.setItem('myitem_' + key, value);
});
// Populate matching hidden fields once the DOM is ready
document.addEventListener('DOMContentLoaded', function () {
Object.keys(fieldMap).forEach(function (key) {
var stored = sessionStorage.getItem('myitem_' + key);
if (!stored) return;
var inputs = document.querySelectorAll(fieldMap[key]);
inputs.forEach(function (input) {
if (!input.value) input.value = stored;
});
});
});
})();
Installation
Step 1: Add the script to your site
Drop the snippet into your site’s global JavaScript file or the <head>. The script needs to run on every page, not just the form page.
Three common ways to do this in WordPress:
- Add it to your theme’s
functions.phpusingwp_headorwp_enqueue_scripts - Paste it into a code snippets plugin like WPCode or Code Snippets, enabled site-wide
- Add it to Google Tag Manager as a Custom HTML tag firing on all pages
Step 2: Add hidden fields to your form
In the Gravity Forms editor:
- Add a Hidden field for each parameter you want to capture: UTM Source, UTM Medium, UTM Campaign, UTM Content, UTM Term, GCLID, GBRAID, WBRAID
- Give each field a clear label. These are internal only, so users won’t see them
- Open the Advanced tab for each field, check Allow field to be populated dynamically, and set the Parameter Name to the matching key (
utm_source,utm_medium, and so on). This enables Gravity Forms’ built-in capture for direct-landing visits
Step 3: Add CSS classes for the script to target
On each hidden field’s Appearance tab, add a CSS Class:
| Hidden field | CSS Class |
|---|---|
| UTM Source | myclass-utm-source |
| UTM Medium | myclass-utm-medium |
| UTM Campaign | myclass-utm-campaign |
| UTM Content | myclass-utm-content |
| UTM Term | myclass-utm-term |
| GCLID | myclass-gclid |
| GBRAID | myclass-gbraid |
| WBRAID | myclass-wbraid |
These classes are what the script uses to find each field.
Step 4: Test the flow
- Visit any page on your site with test parameters:
yoursite.com/blog/some-post/?utm_source=test&utm_campaign=snippet_test - Click an internal link to navigate to the page with your form (do not refresh)
- Fill out the form and submit it
- Open the Gravity Forms entry in your dashboard. The UTM Source and UTM Campaign fields should read
testandsnippet_test
If the fields are empty, inspect the form HTML in your browser’s dev tools and confirm each hidden field’s container has the right CSS class applied.
How it works
The script does two things on every page load.
On page load, it reads the URL. If the current URL contains any of the tracked parameters, it saves each one to sessionStorage under a namespaced key (myitem_utm_source, etc.). This happens immediately, before the DOM finishes rendering.
When the DOM is ready, it populates hidden fields. It finds inputs inside elements with the matching CSS class (for example, .myclass-utm-source input) and fills in the stored value. If the field already has a value from Gravity Forms’ Dynamic Population, the script leaves it alone.
The sessionStorage layer is what lets this work across pages. Once a parameter is captured on the landing page, it stays available for the whole browser session. Even if the visitor browses five pages before reaching the form, the data is still there.
What this snippet does NOT do
Being honest about the limits is more useful than pretending the snippet is complete:
❌ First-touch across multiple sessions. Once the browser session ends, sessionStorage clears. A visitor who clicks your ad today and converts three weeks later from a bookmark has no first-touch data left.
❌ Landing page URLs. The snippet captures parameters, not the first page or conversion page URL. That’s useful attribution context the snippet doesn’t grab.
❌ Referrer fallback. If a visitor arrives from organic search or a partner link with no UTMs, there’s nothing to capture. You’d need to extend the script to fall back to document.referrer.
❌ Cross-device tracking. Session storage is per-browser, per-device. A visitor on mobile today and desktop tomorrow creates two separate sessions with no connection.
❌ Users who block storage APIs. Some privacy tools disable sessionStorage. Those visitors will submit with no captured data.
For single-session campaigns where you control the landing page and the funnel is short, this snippet is enough. For real multi-session attribution, you need more.
For multi-session attribution
If your sales cycle is longer than one browser session, or you want landing page URLs, first-visit dates, and referrer fallback captured on every entry, you need more than a snippet.
Plugin Brewery’s Referral Source Tracking for Gravity Forms handles all of the above out of the box:
- Captures first-touch and last-touch sources across sessions, not just within one
- Stores landing page URLs, first-visit dates, and referrer data on every entry
- Includes referrer fallback when UTM parameters aren’t present
- Works with any form on your site without per-form setup
- Writes directly into Gravity Forms entry fields for clean CRM mapping
👉 Referral Source Tracking for Gravity Forms
If you use WP Forms instead, there’s a WP Forms version with the same functionality.