Custom Context in JavaScript SDK
6 minute read
Overview
Custom context lets you attach enrichment data once and have the JavaScript SDK add it to subsequent events during the current page-load lifetime. Use it for metadata that applies to many events, such as an experiment bucket, account region, application environment, or operator impersonation details.
The SDK adds custom context fields directly under the event’s context object; it doesn’t nest them under a customContext key. Custom context is stored in memory only. It doesn’t persist across page reloads and doesn’t use cookies, localStorage, or sessionStorage.
Custom context is available in the current JavaScript SDK v3 package,@rudderstack/analytics-js. It doesn’t apply to deprecated JavaScript SDK packages or other SDKs.
Set context at load time
Use the context field in the load API options to seed the initial global custom context:
rudderanalytics.load(WRITE_KEY, DATA_PLANE_URL, {
context: {
appTier: 'premium',
region: 'EU'
}
});The SDK applies this context before it processes buffered event calls after a valid load. This load option seeds the stored global custom context; it’s different from the SDK-generated event context fields and from per-call apiOptions context overrides.
If the context value is invalid, the SDK still loads and leaves the stored custom context empty.
Runtime APIs
The JavaScript SDK exposes the following custom context APIs:
| API | Description |
|---|---|
setCustomContext(context) | Deep-merges the provided object into the stored custom context. |
getCustomContext() | Returns a snapshot of the current custom context. Mutating the returned object doesn’t mutate SDK state. |
clearCustomContext() | Clears all stored custom context. |
rudderanalytics.setCustomContext({
experimentBucket: 'B'
});
rudderanalytics.getCustomContext();
rudderanalytics.clearCustomContext();setCustomContext and clearCustomContext are synchronous after the SDK is loaded. getCustomContext() returns the current stored snapshot.
Pre-load buffering
Custom context uses the JavaScript SDK’s pre-load API buffering. If you call setCustomContext or clearCustomContext before a valid load, the SDK buffers the call and replays it with the other buffered API calls after load.
The context load option seeds the stored custom context first. The SDK then replays buffered custom context updates and event calls in order, so each update affects only the event calls that follow it.
For example:
rudderanalytics.load(WRITE_KEY, DATA_PLANE_URL, {
context: {
appTier: 'premium'
}
});
rudderanalytics.track('Before Runtime Update');
rudderanalytics.setCustomContext({
experimentBucket: 'B'
});
rudderanalytics.track('After Runtime Update');
rudderanalytics.clearCustomContext();
rudderanalytics.track('After Clear');In this sequence:
Before Runtime Updateincludescontext.appTier.After Runtime Updateincludescontext.appTierandcontext.experimentBucket.After Cleardoesn’t include the custom context fields.
getCustomContext() isn’t buffered and returns an empty object if you call it before the SDK is loaded.
Deletion semantics
To remove a field from the stored custom context, set that object property to null or explicitly supplied undefined in a setCustomContext call:
rudderanalytics.setCustomContext({
region: 'EU',
account: {
plan: 'pro',
seats: 5
}
});
rudderanalytics.setCustomContext({
region: null,
account: {
plan: undefined
}
});
rudderanalytics.getCustomContext();
// { account: { seats: 5 } }
Deletion markers apply to object properties at any nested object level. If you delete the last field in a nested object, the parent object remains as an empty object. A null or undefined entry inside an array is kept as a value, not treated as a deletion.
To remove an array completely, set the enclosing object property to null or undefined. Passing an empty array doesn’t remove an existing array.
Merge precedence
The SDK builds the final event context using the following precedence, from lowest to highest:
- SDK built-in context.
- Global custom context from the load option or
setCustomContext. - Per-call
apiOptionscontext for the current event.
Per-call apiOptions remain ephemeral. You can pass extra context as additional keys in apiOptions or nested under apiOptions.context. They can override global custom context for one event, but they don’t mutate the stored custom context.
For example:
rudderanalytics.setCustomContext({
account: {
plan: 'pro',
region: 'EU'
}
});
rudderanalytics.track('Order Completed', {
revenue: 30
}, {
account: {
region: 'US'
}
});
rudderanalytics.getCustomContext();
// { account: { plan: 'pro', region: 'EU' } }
In this example, the Order Completed event uses context.account.region as US, but the stored custom context still has region as EU.
Reserved keys
The following root context keys are SDK-managed and can’t be set through custom context or per-event apiOptions:
libraryconsentManagementuserAgentua-chscreen
If you include any of these keys in load-time context, setCustomContext, or per-event apiOptions, the SDK drops the key and emits a console warning. The warning identifies the rejected key but doesn’t log the rejected value.
Reserved-key filtering applies only at the root of the context object. The same names can appear below another custom key.
Other built-in context fields, such as app, campaign, locale, and page, follow the normal merge order and can be extended or overridden.
Event coverage
Custom context applies to subsequent public event API calls:
trackpageidentifygroupalias
Automatic events that use these APIs also receive custom context. SDK diagnostics, error reports, and internal telemetry don’t receive custom context.
The SDK merges custom context into the event payload before sending it, so both cloud-mode and device-mode destinations receive the same final context.
Important considerations
rudderanalytics.reset()doesn’t clear custom context. UseclearCustomContext()explicitly to clear it.- Arrays merge by index. Right-hand entries replace or recursively merge with entries at the same index, while unmatched left-hand entries are retained. For example, merging
{ tags: ['a', 'b', 'c'] }with{ tags: ['x', 'y'] }produces{ tags: ['x', 'y', 'c'] }. - Invalid input, such as a non-plain object or keys like
__proto__,constructor, orprototype, is rejected with a warning and doesn’t change the stored context. - Custom context values are customer-provided event payload data. Avoid adding sensitive data unless your RudderStack configuration and downstream destinations support that use case.
TypeScript types
The public TypeScript definitions use the following custom context types:
type CustomContextValue = string | number | boolean | Date | CustomContext | CustomContextValue[];
type CustomContext = Record<string, CustomContextValue>;
type InputCustomContextValue = string | number | boolean | Date | null | undefined | InputCustomContext | CustomContextValue[];
type InputCustomContext = Record<string, InputCustomContextValue>;Use InputCustomContext when setting custom context because null and undefined are accepted as deletion markers. getCustomContext() returns CustomContext, which doesn’t include deletion markers.
Use cases
Common use cases include:
| Use case | Example custom context |
|---|---|
| Impersonation or acting-agent metadata during operator sessions | { impersonation: { agentId: 'agent-42' } } |
| A/B experiment bucketing applied globally | { experimentBucket: 'B' } |
| App version or environment tagging | { app: { version: '2.4.0', environment: 'production' } } |
| Region or account metadata enrichment | { region: 'EU', account: { plan: 'pro' } } |
rudderanalytics.setCustomContext({
impersonation: {
agentId: 'agent-42'
}
});
rudderanalytics.track('Account Updated');
rudderanalytics.setCustomContext({
impersonation: null
});The Account Updated event includes context.impersonation.agentId. Subsequent events after the final setCustomContext call don’t include the impersonation field.