Skip to content

Commit 1309e56

Browse files
committed
Add cross-domain tracking support
1 parent b133449 commit 1309e56

File tree

3 files changed

+54
-0
lines changed

3 files changed

+54
-0
lines changed

packages/script/src/index.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,12 @@
2929
const sd =
3030
script.getAttribute('data-short-domain') ||
3131
script.getAttribute('data-domain');
32+
const domains = script.getAttribute('data-domains');
3233

3334
return {
3435
apiHost: ah || 'https://api.dub.co',
3536
shortDomain: sd || undefined,
37+
domains: domains ? domains.split(',') : undefined,
3638
attributionModel: am || 'last-click',
3739
cookieOptions: co ? JSON.parse(co) : null,
3840
queryParam: qp || 'via',
@@ -101,6 +103,43 @@
101103
}
102104
}
103105

106+
// Support cross-domain tracking
107+
function appendCrossDomainClickId(clickId) {
108+
const cookie = clickId || getCookie(CLICK_ID);
109+
110+
if (!cookie) {
111+
return;
112+
}
113+
114+
let { domains } = getOptions(script);
115+
116+
if (!domains || domains.length === 0) {
117+
return;
118+
}
119+
120+
const currentDomain = window.location.hostname.replace(/^www\./, '');
121+
122+
domains = domains.filter((domain) => domain !== currentDomain);
123+
124+
const selector = domains.map((domain) => `a[href*="${domain}"]`).join(',');
125+
126+
if (!selector || selector.length === 0) {
127+
return;
128+
}
129+
130+
const links = document.querySelectorAll(selector);
131+
132+
if (!links || links.length === 0) {
133+
return;
134+
}
135+
136+
links.forEach((link) => {
137+
const url = new URL(link.href);
138+
url.searchParams.set(CLICK_ID, cookie);
139+
link.href = url.toString();
140+
});
141+
}
142+
104143
// Function to check for { keys } in the URL and update cookie if necessary
105144
function watchForQueryParams() {
106145
const searchParams = new URLSearchParams(window.location.search);
@@ -111,6 +150,7 @@
111150

112151
if (clickId) {
113152
checkCookieAndSet(clickId);
153+
appendCrossDomainClickId(clickId);
114154
return;
115155
}
116156

@@ -148,10 +188,12 @@
148188
}
149189
const { clickId } = await res.json(); // Response: { clickId: string }
150190
checkCookieAndSet(clickId);
191+
appendCrossDomainClickId(clickId);
151192
});
152193
}
153194

154195
watchForQueryParams();
196+
appendCrossDomainClickId();
155197

156198
// Listen for URL changes in case of SPA where the page doesn't reload
157199
window.addEventListener('popstate', watchForQueryParams);

packages/web/src/generic.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ function inject(props: AnalyticsProps): void {
2727
script.setAttribute('data-short-domain', props.shortDomain);
2828
}
2929

30+
if (props.domains) {
31+
script.setAttribute('data-domains', props.domains.join(','));
32+
}
33+
3034
if (props.attributionModel) {
3135
script.setAttribute('data-attribution-model', props.attributionModel);
3236
}

packages/web/src/types.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,14 @@ export interface AnalyticsProps {
1313
*/
1414
shortDomain?: string;
1515

16+
/**
17+
* An array of domains for cross-domain tracking. When configured, a `dub_id` query parameter
18+
* will be automatically appended to all outbound links targeting these domains to enable
19+
* cross-domain tracking across different domains.
20+
* @example ['example.com', 'app.example.io']
21+
*/
22+
domains?: string[];
23+
1624
/**
1725
* The Attribution Model to use for the analytics event.
1826
*

0 commit comments

Comments
 (0)