From 74ca344af00598fc1997427aeb0205d83eb385ac Mon Sep 17 00:00:00 2001 From: Gianpaolo Date: Wed, 18 Feb 2026 10:16:36 +0100 Subject: [PATCH 1/3] feat: add useUtmParams hook for reading and persisting UTM parameters --- src/hooks/useUtmParams.ts | 42 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 src/hooks/useUtmParams.ts diff --git a/src/hooks/useUtmParams.ts b/src/hooks/useUtmParams.ts new file mode 100644 index 000000000..3c70c572a --- /dev/null +++ b/src/hooks/useUtmParams.ts @@ -0,0 +1,42 @@ +import { useSearchParams } from 'react-router-dom'; + +const UTM_KEYS = [ + 'utm_source', + 'utm_medium', + 'utm_campaign', + 'utm_content', + 'utm_term', + 'platform', +] as const; + +const STORAGE_KEY = 'utmParams'; + +type UtmParams = Partial>; + +function getStoredUtmParams(): UtmParams { + try { + const stored = sessionStorage.getItem(STORAGE_KEY); + return stored ? JSON.parse(stored) : {}; + } catch { + return {}; + } +} + +export function useUtmParams(): UtmParams { + const [searchParams] = useSearchParams(); + + const urlParams: UtmParams = {}; + UTM_KEYS.forEach((key) => { + const value = searchParams.get(key); + if (value) { + urlParams[key] = value; + } + }); + + if (Object.keys(urlParams).length > 0) { + sessionStorage.setItem(STORAGE_KEY, JSON.stringify(urlParams)); + return urlParams; + } + + return getStoredUtmParams(); +} From b49dc5a620be588f2a152ca9fde10694cef4c006 Mon Sep 17 00:00:00 2001 From: Gianpaolo Date: Wed, 18 Feb 2026 10:19:28 +0100 Subject: [PATCH 2/3] feat: integrate useUtmParams in Track component for UTM propagation to all providers --- src/common/Track.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/common/Track.tsx b/src/common/Track.tsx index 6292cc7b4..84c8c08a2 100644 --- a/src/common/Track.tsx +++ b/src/common/Track.tsx @@ -3,6 +3,7 @@ import { Helmet } from 'react-helmet'; import { useLocation } from 'react-router-dom'; import { useGetUsersMeQuery } from 'src/features/api'; import { useActiveWorkspace } from 'src/hooks/useActiveWorkspace'; +import { useUtmParams } from 'src/hooks/useUtmParams'; import { useAnalytics } from 'use-analytics'; export const Track = ({ @@ -19,7 +20,7 @@ export const Track = ({ const { data: userData, isLoading, isSuccess } = useGetUsersMeQuery(); const { activeWorkspace } = useActiveWorkspace(); const { track, identify, page } = useAnalytics(); - const utmSource = sessionStorage.getItem('utmSource'); + const utmParams = useUtmParams(); const location = useLocation(); const defaultMeta = [ @@ -39,7 +40,7 @@ export const Track = ({ ]; useEffect(() => { - page(); + page(utmParams); }, [location]); const helmet = () => ( @@ -64,7 +65,7 @@ export const Track = ({ email: userData.email, company: activeWorkspace.company, workspace: activeWorkspace, - ...((utmSource && { utm_source: utmSource }) || {}), + ...utmParams, }); track( From 5ac8b8fcd786703050f364e0cdd4fa7b1a4530bf Mon Sep 17 00:00:00 2001 From: Gianpaolo Date: Wed, 18 Feb 2026 10:22:52 +0100 Subject: [PATCH 3/3] refactor: remove legacy utm_source handling from JoinPage in favor of global useUtmParams --- src/pages/JoinPage/useJoinSubmit.tsx | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/pages/JoinPage/useJoinSubmit.tsx b/src/pages/JoinPage/useJoinSubmit.tsx index e159403fc..675c8f012 100644 --- a/src/pages/JoinPage/useJoinSubmit.tsx +++ b/src/pages/JoinPage/useJoinSubmit.tsx @@ -16,7 +16,6 @@ export function useJoinSubmit(isInvited: boolean) { const sendGTMevent = useSendGTMevent({ loggedUser: false }); const templateParam = searchParams.get('template'); - const utmSource = searchParams.get('utm_source'); let templateId: number | undefined; if (templateParam !== null) { @@ -30,10 +29,6 @@ export function useJoinSubmit(isInvited: boolean) { templateId = parsed; } - if (utmSource !== null) { - sessionStorage.setItem('utmSource', utmSource); - } - const onSubmit = useCallback( async ( values: JoinFormValues,