RudderStack
RudderStack collects customer data from websites and routes it through your RudderStack data plane to downstream destinations. The rudderstack() helper creates RudderStack's window.rudderanalytics v3 queue, queues the required load() call with your write key and data plane URL, optionally queues the initial page() call, and loads the browser SDK when measurement consent is available.
Integrate with c15t
import { type ReactNode } from 'react';
import { ConsentManagerProvider } from '@c15t/react';
import { rudderstack } from '@c15t/scripts/rudderstack';
const scripts = [
rudderstack({
writeKey: 'WRITE_KEY',
dataPlaneUrl: 'https://example.dataplane.rudderstack.com',
}),
];
export function ConsentProvider({ children }: { children: ReactNode }) {
return (
<ConsentManagerProvider
options={{
mode: 'hosted',
backendURL: 'https://your-instance.c15t.dev',
scripts,
}}
>
{children}
</ConsentManagerProvider>
);
}How c15t loads it
- Category:
measurement(Analytics) - Loads when: measurement consent is granted
- On revocation: unloaded - c15t removes the script element from the DOM and clears RudderStack globals until consent is granted again.
The helper maps RudderStack's v3 browser snippet into the manifest engine:
rudderstack({
writeKey: 'WRITE_KEY',
dataPlaneUrl: 'https://example.dataplane.rudderstack.com',
});It creates window.rudderanalytics, defines the v3 snippet queue methods, queues:
window.rudderanalytics.load(
'WRITE_KEY',
'https://example.dataplane.rudderstack.com',
{}
);
window.rudderanalytics.page();and loads:
https://cdn.rudderlabs.com/v3/modern/rsa.min.jswriteKey and dataPlaneUrl are both required and must be non-empty strings after trimming. dataPlaneUrl must be a valid HTTPS URL.
Configure load options
Pass loadOptions to provide RudderStack SDK options as the third load() argument. Values must be JSON-serializable: plain objects, arrays, strings, numbers, booleans, and null.
import { rudderstack } from '@c15t/scripts/rudderstack';
rudderstack({
writeKey: 'WRITE_KEY',
dataPlaneUrl: 'https://example.dataplane.rudderstack.com',
loadOptions: {
useBeacon: true,
plugins: ['BeaconQueue'],
},
});Do not pass functions, Dates, Maps, Sets, class instances, symbols, or other non-JSON values in loadOptions.
Configure page tracking
By default the helper queues rudderanalytics.page() before the vendor bundle loads. If you want to handle page views yourself, set trackPageView to false.
rudderstack({
writeKey: 'WRITE_KEY',
dataPlaneUrl: 'https://example.dataplane.rudderstack.com',
trackPageView: false,
});To proxy or self-host the loader, pass a custom URL:
rudderstack({
writeKey: 'WRITE_KEY',
dataPlaneUrl: 'https://example.dataplane.rudderstack.com',
scriptUrl: 'https://analytics.example.com/rsa.min.js',
});Consent behavior
RudderStack exposes a consent() API for its own consent-management flow and pre-consent event handling. That API is not a simple runtime opt-out or revocation API for c15t to call after a user withdraws measurement consent.
c15t therefore uses the post-consent model recommended by RudderStack's docs: it does not load the SDK until measurement consent is granted, and it unloads the script if that consent is later revoked.
Why c15t blocks the load instead of mapping consent into RudderStack
Some integrations — Google Tag Manager is the clearest example — load immediately and receive c15t's consent state through the vendor's own consent API (Google Consent Mode v2). That model is only safe when the vendor API provides denied-by-default semantics where nothing identifying is stored or transmitted before consent.
Loading the RudderStack SDK does not provide that guarantee on its own: once loaded normally it writes its anonymousId cookie and delivers events to your data plane, and the consent() API filters which downstream destinations receive those events rather than preventing collection. Even RudderStack's pre-consent mode defaults to events.delivery: 'immediate', which still sends pre-decision events. "Loaded but consent-filtered" is still collection, so c15t treats blocking the load as the only default that reliably honors a missing or denied measurement consent.
RudderStack v3 does offer a pre-consent mode (preConsent load options with storage disabled and buffered delivery) designed for CMP integrations, which allows a GTM-style flow. c15t supports it as an explicit opt-in — see below. It is not the default because it runs vendor code before consent and depends on vendor-side configuration (consent IDs on every destination) that c15t cannot verify from the browser.
The same reasoning applies to the other customer-data-platform helpers (Segment, Hightouch): their consent surfaces are destination filters, not collection gates, so those helpers block the load too.
Opt-in: pre-consent mode with consent ID mapping
Pass consentManagement to make c15t the consent provider for RudderStack's pre-consent flow:
import { rudderstack } from '@c15t/scripts/rudderstack';
rudderstack({
writeKey: 'WRITE_KEY',
dataPlaneUrl: 'https://example.dataplane.rudderstack.com',
consentManagement: {
// c15t category → RudderStack consent IDs (from your destination settings)
mapping: {
measurement: ['product-analytics'],
marketing: ['ad-destinations'],
},
},
});In this mode:
- The SDK loads immediately for every visitor, but inert:
preConsentis enabled with storage strategynone(no cookies, no localStorage) and buffered event delivery — nothing reaches your data plane before a consent decision. - On every consent decision and change, c15t calls
rudderanalytics.consent()withallowedConsentIds/deniedConsentIdspartitioned from your mapping. The initial signal is queued before the SDK loads, so consent state is known the moment the SDK initializes. - Consent revocation re-signals with the denied IDs instead of unloading the script.
Event attribution. This is the reason to opt in: events fired before the user interacts with the consent banner (the initial page() call, early product events) are buffered and delivered once consent is granted, so consenting users keep their full journey. In the default blocked-load mode those pre-consent events are simply lost. If you want session stitching across the consent boundary as well, pass your own preConsent in loadOptions with storage strategy 'session' — c15t keeps your storage choice but always forces preConsent.enabled, buffered event delivery, and the custom consent provider, since any of those falling back to SDK defaults would leak pre-consent events.
Requirements and caveats:
- Every destination in your RudderStack workspace must be assigned the consent IDs used in the mapping. Destinations without consent IDs receive events regardless of consent — that is RudderStack behavior c15t cannot detect or prevent from the browser.
- Users who never consent have their buffered events discarded; nothing is persisted for them.
- The daily script vendor monitor probes this mode against the live SDK: it loads RudderStack with denied consent in a real browser and asserts zero data plane requests and zero
rl_*storage, so a vendor-side change to pre-consent semantics is caught automatically.
Choosing a consent model for CDPs
- Default (blocked load) — strictest interpretation of consent; no vendor code runs pre-consent. Pre-consent events are lost. Right when compliance posture outweighs attribution.
- Pre-consent mode (
consentManagement) — vendor-sanctioned CMP flow; pre-consent events are buffered and attributed for consenting users. Requires disciplined destination consent-ID configuration and accepts vendor code running before consent, with the live monitor verifying its inertness daily.
Tracking events in your app
c15t gates the RudderStack browser SDK from loading until measurement consent is granted. Your application code that calls RudderStack's runtime API (window.rudderanalytics.track, identify, etc.) is not automatically gated - window.rudderanalytics does not exist until the script is loaded, so unguarded calls before consent throw.
Guard event calls by checking consent state. From React:
import { useCallback } from 'react';
import { useConsentManager } from '@c15t/react';
function SignupExample() {
const { has } = useConsentManager();
const trackSignup = useCallback(() => {
if (has('measurement')) {
window.rudderanalytics?.track('Signup Completed', { plan: 'pro' });
}
}, [has]);
}From plain JavaScript:
import { getOrCreateConsentRuntime } from 'c15t';
const { consentStore } = getOrCreateConsentRuntime();
if (consentStore.getState().has('measurement')) {
window.rudderanalytics?.track('Signup Completed', { plan: 'pro' });
}Types
RudderStackOptions
Warning: ExtractedTypeTable: Could not extract "RudderStackOptions" from "./packages/scripts/src/vendors/analytics/rudderstack.ts" using base path "/vercel/path0/apps/c15t-docs/.leadtype/c15t". Verify the path/name and that the file is included by your tsconfig.
Script
Warning: ExtractedTypeTable: Could not extract "Script" from "./packages/core/src/libs/script-loader/types.ts" using base path "/vercel/path0/apps/c15t-docs/.leadtype/c15t". Verify the path/name and that the file is included by your tsconfig.