UTM parameters are how you tell paid search, paid social, email, and partner referrals apart. Lose them and every marketing dollar collapses into a single undifferentiated (direct) / (none) bucket, making channel ROI impossible to calculate. If UTMs are stripped by a client-side redirect before the analytics SDK captures them, or if you chose an SDK that does not read them automatically, every campaign you spent money on looks identical to organic traffic in the dashboard.
High because lost campaign attribution makes marketing spend unmeasurable and unchannelable.
Confirm your SDK captures UTMs natively — GA4, PostHog, Mixpanel, and Segment all do. For Plausible or a custom setup, parse them on landing in lib/analytics.ts:
const params = new URLSearchParams(window.location.search)
analytics.identify({ lastUTMSource: params.get('utm_source'), lastUTMCampaign: params.get('utm_campaign') })
Persist to sessionStorage so the attribution survives navigation to the conversion page.
ID: marketing-analytics.core-analytics.utm-parameter-handling
Severity: high
What to look for: Count every UTM parameter reference in the codebase. UTM parameters (utm_source, utm_medium, utm_campaign, utm_term, utm_content) carry campaign attribution data. Check whether the project:
useSearchParams, router.query, new URLSearchParams(window.location.search), or similar)sessionStorage or localStorage for multi-touch attributionPass criteria: UTM parameters are either automatically captured by the analytics SDK on page load (GA4, Segment, PostHog do this natively) OR at least 1 explicit UTM reading/forwarding code location exists. Pass if the analytics SDK used is known to capture UTMs automatically. Quote the SDK name or the UTM capture code location in the detail.
Fail criteria: The analytics SDK used does NOT automatically capture UTMs (e.g., a custom minimal analytics setup, or Plausible which does not capture UTM details by default in all configurations) AND no explicit UTM capture code exists. An analytics SDK that reads UTMs only from server-side requests does not count as client-side UTM capture.
Skip (N/A) when: No analytics is present (script-present failed).
Detail on fail: "Plausible Analytics detected with no custom UTM forwarding code. Plausible records the referrer but does not capture individual UTM parameter values by default — campaign-level attribution data will be incomplete."
Remediation: Most major analytics SDKs (GA4, PostHog, Mixpanel, Segment) automatically read UTM parameters from the page URL. Verify your SDK does this. If not, capture them explicitly:
// lib/analytics.ts
export function captureUTMParams() {
const params = new URLSearchParams(window.location.search)
const utms = {
source: params.get('utm_source'),
medium: params.get('utm_medium'),
campaign: params.get('utm_campaign'),
}
if (utms.source) {
analytics.identify({ lastUTMSource: utms.source, lastUTMCampaign: utms.campaign })
}
}
Call this on initial page load. Consider storing UTMs in sessionStorage to attribute conversions that happen on subsequent pages.