---
title: RudderStack
description: Load RudderStack's JavaScript SDK with c15t and gate the browser
  SDK behind measurement consent.
group: integrations
icon: rudderstack
lastModified: "2026-08-04T19:55:26+01:00"
---
[RudderStack](https://www.rudderstack.com/) 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

**React**

```tsx
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>
  );
}
```

**Next.js**

```tsx
'use client';

import { type ReactNode } from 'react';
import { ConsentManagerProvider } from '@c15t/nextjs';
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: '/api/c15t',
        scripts,
      }}
    >
      {children}
    </ConsentManagerProvider>
  );
}
```

**JavaScript**

```ts
import { getOrCreateConsentRuntime } from 'c15t';
import { rudderstack } from '@c15t/scripts/rudderstack';

getOrCreateConsentRuntime({
  mode: 'hosted',
  backendURL: 'https://your-instance.c15t.dev',
  scripts: [
    rudderstack({
      writeKey: 'WRITE_KEY',
      dataPlaneUrl: 'https://example.dataplane.rudderstack.com',
    }),
  ],
});
```

## 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:

```ts
rudderstack({
  writeKey: 'WRITE_KEY',
  dataPlaneUrl: 'https://example.dataplane.rudderstack.com',
});
```

It creates `window.rudderanalytics`, defines the v3 snippet queue methods, queues:

```ts
window.rudderanalytics.load(
  'WRITE_KEY',
  'https://example.dataplane.rudderstack.com',
  {}
);
window.rudderanalytics.page();
```

and loads:

```txt
https://cdn.rudderlabs.com/v3/modern/rsa.min.js
```

`writeKey` 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`.

```ts
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`.

```ts
rudderstack({
  writeKey: 'WRITE_KEY',
  dataPlaneUrl: 'https://example.dataplane.rudderstack.com',
  trackPageView: false,
});
```

To proxy or self-host the loader, pass a custom URL:

```ts
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:

```ts
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**: `preConsent` is enabled with storage strategy `none` (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()` with `allowedConsentIds`/`deniedConsentIds` partitioned 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](https://github.com/c15t/c15t/issues/899) 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:

```tsx
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:

```ts
import { getOrCreateConsentRuntime } from 'c15t';

const { consentStore } = getOrCreateConsentRuntime();

if (consentStore.getState().has('measurement')) {
  window.rudderanalytics?.track('Signup Completed', { plan: 'pro' });
}
```

## Types

### RudderStackOptions

|Property|Value|
|:--|:--|
|Type Name|\`RudderStackOptions\`|
|Source Path|\`./packages/scripts/src/vendors/analytics/rudderstack.ts\`|

\*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

|Property|Value|
|:--|:--|
|Type Name|\`Script\`|
|Source Path|\`./packages/core/src/libs/script-loader/types.ts\`|

\*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.\*
