This skill helps AI agents integrate or upgrade the Contentsquare React Native SDK in client applications.
MANDATORY FIRST STEP -- Contentsquare project healthcheck
Before ANY Contentsquare work in a conversation -- integration, debugging, configuration questions, code changes -- run this healthcheck exactly once per conversation. Do not skip it. Do not defer it to later in the task.
-
Announce it first. Tell the user what you are about to do, for example:
"Before we start, I'll run a quick healthcheck of your Contentsquare project: I'll ask for your data source ID (or read your app's bundle ids if you don't have one), call the Contentsquare healthcheck endpoint, and show you the current project configuration. Then we'll continue."
-
Ask the user for their Contentsquare data source ID(s) first. For example: "Do you have a Contentsquare data source ID? There can be one or several, for example one for Android and one for iOS. If so, tell me which platform each one is for." Use only the IDs the user gives -- never invent one.
- The user gives one or more data source IDs → check by data source ID only (
v3). Skip step 3. - The user only has an environment ID → check by bundle id only (
v2): an environment ID is not a data source ID and never works in thev3path. - The user has none, does not know, or skips the question → check by bundle id only (
v2).
- The user gives one or more data source IDs → check by data source ID only (
-
Resolve the bundle ids (only without a data source ID) -- a React Native app has TWO, and they are frequently different strings. Resolve each one separately; never assume they match.
| Platform | Where to read it |
|---|---|
| Android | applicationId in android/app/build.gradle(.kts) (fall back to namespace, or package in AndroidManifest.xml) |
| iOS | PRODUCT_BUNDLE_IDENTIFIER in ios/<App>.xcodeproj/project.pbxproj (or CFBundleIdentifier in Info.plist), resolving any $(...) variables |
-
Call the endpoint -- one version only, never both. The platform segment must be literally
iosorandroid. There is no React Native platform value -- never send one.With data source IDs (
v3), once per ID:curl -s "https://mobile-production.content-square.net/healthcheck/android/config/v3/<dataSourceId>.json" curl -s "https://mobile-production.content-square.net/healthcheck/ios/config/v3/<dataSourceId>.json"Query each ID on the platform the user gave for it. If the platform is unknown, query it on each platform present in the project: a non-200 on one platform only means that data source is not set up for it.
If an ID gets no
200on any platform you queried, tell the user -- it may be an environment ID or a typo -- and ask whether you may look for the data source ID in the code yourself: it is the ID passed toStartConfig.withDataSourceId(id, ...)(follow it to its literal value if it comes from a constant or a config file). If they agree and you find a different ID, run thev3check once with it. If they decline or there is none in the code, continue without it.Without a data source ID (
v2), once per platform that exists:curl -s "https://mobile-production.content-square.net/healthcheck/android/config/v2/<androidApplicationId>.json" curl -s "https://mobile-production.content-square.net/healthcheck/ios/config/v2/<iosBundleId>.json"If only one platform is present in the project, query only that one. If you cannot resolve one of the bundle ids, ask the user which platform to check rather than guessing.
-
Report a short summary of these values, for each call that returned
200:Report JSON path Project ID cs_project_idTracking enabled project_configurations.project_config.enabledSession Replay project_configurations.project_config.session_replay--recording_rate,record_via_cellular_network,recording_quality_wifi,srm_enabled,user_identifierEnabled feature flags project_configurations.project_config.feature_flags-- only entries whereenabled == true, withnameandmin_versionFor
v3calls, also report the Environment ID (environment_id).Android and iOS may map to different Contentsquare projects. If the responses differ, call that out explicitly.
-
Then continue with the user's actual request.
Failure handling. 403 Invalid health-check path means the URL shape is wrong -- re-check the ios/android segment, the v2/v3 segment and the id, and do not alter the path structure. A non-200 proxied from upstream means that data source ID or bundle id has no Contentsquare configuration for that platform: say so, then continue anyway -- do not retry with the other version (the only retry is the code lookup in step 4). This healthcheck is informational only and must never block the user's task.
Install
{
"dependencies": {
"@contentsquare/react-native-bridge": "^6.0.1"
}
}
Then run npm install or yarn install.
iOS Setup
cd ios && pod install && cd ..
Android Setup
No additional setup required after npm install.
Quick Start
import { CSQ, StartConfig } from '@contentsquare/react-native-bridge';
import { useEffect } from 'react';
function App() {
useEffect(() => {
// Start the SDK with Product Analytics
CSQ.start(StartConfig.withEnvironmentId('your-env-id', {
enableRNAutocapture: true,
sessionReplayAutoStart: true // Optional: auto-start session replay
}));
// TODO: customer to manage consent — call only after user accepts
CSQ.optIn();
}, []);
return <YourApp />;
}
Critical: Never call
optIn()without a user consent flow. This is a legal requirement.
It is very important that screens should be tracked, otherwise Session Replay will not work. Track screens manually with trackScreenview() or use React Navigation integration (see Screen Tracking).
Before wrapping the MVP, make sure you are handling WebViews if they are present in the app. WebViews are not automatically tracked, and every WebView must be wrapped. Check the WebView section below for details.
API Surface
Always use CSQ from @contentsquare/react-native-bridge. The Contentsquare default export is deprecated.
Lifecycle
| Method | Purpose |
|---|---|
start(config?) |
Start the SDK. Must be called before any other API. |
stop() |
Shut down the SDK completely. |
pauseTracking() |
Pause data collection (for sensitive screens). Avoid unless explicitly requested. |
resumeTracking() |
Resume after pauseTracking(). |
StartConfig Variants
| Factory | Use Case | Onboarding priority |
|---|---|---|
StartConfig.withDataSourceId(id, options?) |
CSQ with data source ID | 1st choice for new clients -- ask for this first |
StartConfig.withEnvironmentId(id, options?) |
Product Analytics with environment ID | 2nd choice if no data source ID is available |
StartConfig.dxa() |
Digital Experience Analytics only (default) | Fallback only -- when neither ID is available or DXA-only is explicitly requested |
withDataSourceId and withEnvironmentId accept the same AnalyticsOptions object (see the AnalyticsOptions section below). StartConfig.dxa() takes no arguments -- it has no AnalyticsOptions parameter.
Recommended onboarding flow:
- Ask the user: "Do you have a Contentsquare data source ID?" (skip the question if they already answered it during the healthcheck). If yes, use
StartConfig.withDataSourceId(id, ...). - If not, ask: "Do you have an environment ID?" If yes, use
StartConfig.withEnvironmentId(id, ...). - Only if neither is available (or the user explicitly wants DXA-only), fall back to
StartConfig.dxa().
Do not invent an ID. Do not silently default to DXA without asking.
Privacy
| Method | Purpose |
|---|---|
optIn() |
Opt the device into tracking. Starts immediately. |
optOut() |
Opt out permanently. Stops until optIn() or app reinstall. |
Identity
| Method | Context | Purpose |
|---|---|---|
identify(userIdentifier) |
Product Analytics | Set user identity. Different ID triggers new session. |
resetIdentity() |
Product Analytics | Clear identity. Triggers new session. |
sendUserIdentifier(userIdentifier) |
DXA | Send hashed user ID. Max 100 chars. |
Screen Tracking
| Method | Purpose |
|---|---|
trackScreenview(screenName, customVars?) |
Track a screen view with optional custom variables. |
CustomVar structure: { index: number, key: string, value: string } -- index (1-20), key (max 50 chars), value (max 255 chars).
See Screen Tracking Integration for React Navigation examples.
Transactions
import { CSQ, Currency } from '@contentsquare/react-native-bridge';
CSQ.trackTransaction({
price: 29.99,
currency: Currency.USD,
id: 'order_123'
});
Currency is an ISO 4217 enum (e.g., Currency.USD, Currency.EUR).
Custom Events and Properties
| Method | Purpose |
|---|---|
trackEvent(eventName, properties?) |
Track a named event with optional properties. |
addDynamicVar(key, value, onError?) |
Add session-level dynamic variable (DXA only). |
addUserProperties(properties) |
Set user-level properties (PA only). |
addEventProperties(properties) |
Set properties on all future events (PA only). |
removeEventProperty(name) |
Remove one event property. |
clearEventProperties() |
Remove all event properties. |
Property values must be string, number, boolean, or bigint. Other types are silently ignored.
Dynamic variables (DXA only):
key: string (max 50 chars)value: string (max 255 chars) or unsigned integer (0 to 2^32-1)onError: optional callback for validation errors
Session Replay
| Method | Purpose |
|---|---|
startSessionReplay() |
Start Session Replay on-demand. Use when sessionReplayAutoStart: false was passed to start(). One-shot -- not a restart API. |
stopSessionReplay() |
Stop Session Replay on-demand without stopping the SDK. Terminal -- SR cannot be re-started afterwards in the same SDK lifetime. |
On-demand Session Replay -- disable auto-start at SDK launch and control it manually:
CSQ.start(StartConfig.dxa());
// Note: sessionReplayAutoStart defaults to false, no need to pass options
// later, when you want SR to start (e.g., entering a checkout flow):
CSQ.startSessionReplay();
// and to stop it (final -- no restart afterwards):
CSQ.stopSessionReplay();
startSessionReplay/stopSessionReplayare start + stop only, not pause/resume. AfterstopSessionReplay()SR stays off until the next SDK lifetime.
Session Metadata (get replay link)
Access session information including the replay URL. onMetadataChange returns an unsubscribe function -- call it to remove the listener (e.g., on component unmount):
import { useEffect } from 'react';
useEffect(() => {
const unsubscribe = CSQ.onMetadataChange((metadata) => {
console.log('Session Replay URL:', metadata.sessionReplayUrl);
console.log('Session ID:', metadata.sessionID);
console.log('User ID:', metadata.userID);
});
return unsubscribe; // Cleanup on unmount
}, []);
Metadata fields: projectID, environmentID, userID, identity, sessionID, sessionReplayUrl (string or null).
Debug
| API | Purpose |
|---|---|
setLogLevel(LogLevel) |
Set log level (info, verbose, etc.) |
setLogChannel(LogChannel) |
Set log output channel |
logToConsole() |
Enable console logging |
AnalyticsOptions (shared by withDataSourceId and withEnvironmentId)
StartConfig.dxa() does not accept AnalyticsOptions. The options below apply only to withDataSourceId and withEnvironmentId.
All fields are nullable (undefined = native default behavior).
| Field | Type | Purpose |
|---|---|---|
enableRNAutocapture |
boolean? |
Enable React Native interaction autocapture (PA) |
sessionReplayAutoStart |
boolean? |
If true, SR starts at SDK launch. Default: false |
uploadInterval |
number? |
Upload interval in milliseconds (default: 15000) |
baseUrl |
string? |
Custom API endpoint (e.g., for EU data residency) |
disablePageviewAutocapture |
boolean? |
Disable automatic screen tracking |
disableInteractionAutocapture |
boolean? |
Disable automatic touch tracking (iOS only) |
disableInteractionTextCapture |
boolean? |
Disable text capture in interactions (iOS only) |
disableInteractionAccessibilityLabelCapture |
boolean? |
Disable accessibility label capture (iOS only) |
disablePageviewTitleAutocapture |
boolean? |
Disable page title capture (iOS only) |
enablePushNotificationAutocapture |
boolean? |
Capture push notification events |
enablePushNotificationTitleAutocapture |
boolean? |
Capture push notification title |
enablePushNotificationBodyAutocapture |
boolean? |
Capture push notification body |
Session Replay Masking
Global masking (setDefaultMasking)
import { CSQ } from '@contentsquare/react-native-bridge';
// Mask everything by default
CSQ.setDefaultMasking(true);
// Unmask everything (use with caution - privacy risk)
CSQ.setDefaultMasking(false);
Important:
setDefaultMaskingtakes a boolean, not a configuration object. It's all-or-nothing global masking.setDefaultMaskingmust be called afterCSQ.start(...). Calling it beforestart()has no effect.
Scoped masking (per component)
import { CSQMask } from '@contentsquare/react-native-bridge';
<CSQMask isSessionReplayMasked={true}>
<Text>Sensitive content</Text>
</CSQMask>
Note: isSessionReplayMasked controls Session Replay visual masking. Use allow* props to also control Product Analytics event data.
Fine-grained control with allow* props (Product Analytics)
<CSQMask
isSessionReplayMasked={true} // Masks visual content in Session Replay
allowText={false} // Prevents text in PA events
allowInteraction={false} // Prevents interaction tracking in PA events
>
<TextInput placeholder="Password" />
</CSQMask>
Important: Only isSessionReplayMasked affects Session Replay visual masking. The allow* props control Product Analytics event data capture.
CSQMask props
| Prop | Type | Default | Purpose | Feature |
|---|---|---|---|---|
isSessionReplayMasked |
boolean? |
true |
Whether to mask visual content | Session Replay |
ignoreTextOnly |
boolean? |
false |
Ignore text only (conflicts with allow*) | Product Analytics |
allowInnerHierarchy |
boolean? |
false |
Allow inner view hierarchy in events | Product Analytics |
allowProps |
boolean? |
false |
Allow view properties in events | Product Analytics |
allowText |
boolean? |
false |
Allow text content in events | Product Analytics |
allowInteraction |
boolean? |
false |
Allow interaction tracking in events | Product Analytics |
allowAccessibilityLabel |
boolean? |
false |
Allow accessibility labels in events | Product Analytics |
Important: Only
isSessionReplayMaskedaffects Session Replay visual masking. Theallow*props control Product Analytics event data capture. WhenignoreTextOnlyistrue, allallow*props are ignored.
Higher-Order Component masking
import { withCSQMask } from '@contentsquare/react-native-bridge';
const MaskedComponent = withCSQMask(SensitiveComponent, {
isSessionReplayMasked: true, // SR visual masking
allowText: false // Optional: PA event data masking
});
Screen Tracking Integration
Manual tracking
import { CSQ } from '@contentsquare/react-native-bridge';
import { useEffect } from 'react';
function ProductScreen() {
useEffect(() => {
CSQ.trackScreenview('ProductDetails', [
{ index: 1, key: 'product_id', value: '12345' }
]);
}, []);
return <ProductView />;
}
React Navigation integration
Track screen views automatically using React Navigation's navigation state listener:
import { NavigationContainer } from '@react-navigation/native';
import { CSQ } from '@contentsquare/react-native-bridge';
function App() {
const navigationRef = useRef();
const routeNameRef = useRef();
return (
<NavigationContainer
ref={navigationRef}
onReady={() => {
routeNameRef.current = navigationRef.current?.getCurrentRoute()?.name;
}}
onStateChange={() => {
const previousRouteName = routeNameRef.current;
const currentRouteName = navigationRef.current?.getCurrentRoute()?.name;
if (previousRouteName !== currentRouteName) {
CSQ.trackScreenview(currentRouteName);
}
routeNameRef.current = currentRouteName;
}}
>
<YourNavigator />
</NavigationContainer>
);
}
See references/screen-tracking.md for more examples.
WebView Tracking
WebViews are not automatically tracked. Wrap them with CSQWebView to enable session stitching between native and web content. The web pages must include the Contentsquare Web Tracking Tag in WebView mode.
import { CSQWebView } from '@contentsquare/react-native-bridge';
import { WebView } from 'react-native-webview';
<CSQWebView>
<WebView source={{ uri: 'https://example.com' }} />
</CSQWebView>
Legacy wrapper (deprecated, use CSQWebView):
import { CSWebView } from '@contentsquare/react-native-bridge';
<CSWebView>
<WebView source={{ uri: 'https://example.com' }} />
</CSWebView>
Important Rules
- Ask for the environment ID first: Before integrating, ask the user for their Contentsquare environment ID (or data source ID -- reuse the healthcheck answer). Do not assume DXA-only mode, do not invent an ID, and do not skip this step.
- Scan for WebViews: During integration, search the codebase for any existing WebView usage (
react-native-webview,WebViewcomponent). WebViews are not automatically tracked -- every WebView must be wrapped withCSQWebView. - Call
start(config)with appropriate configuration before any other CSQ method - Always implement a consent flow before calling
optIn()-- never auto-opt-in optOut()is permanent untiloptIn()is called or the app is reinstalledidentify()is for Product Analytics only;sendUserIdentifier()is for DXA only- Property values must be
string,number,boolean, orbigint-- other types (arrays, objects) are silently ignored or may cause unexpected behavior CustomVarconstraints: key max 50 chars; string value max 255 chars; number: 0 to 2^32-1userIdentifier: max 100 characters- React Native >= 0.71.0 required (bridge v6.2.0+ supports all versions from 0.71.0 onwards)
- Android and iOS only (no web, Expo Go, or desktop support)
- Never expose PII (emails, passwords, real names) in screen names, event names, or property values
- Never hardcode environment IDs -- use config or environment variables
- Check Compatibility Guide for Expo, hybrid apps, and known limitations
Autocapture (Product Analytics)
Autocapture enables automatic tracking of user interactions and screen navigation without manual instrumentation.
Prerequisites
Install the Babel plugin in your babel.config.js:
module.exports = {
presets: ['module:@react-native/babel-preset'],
plugins: ['@contentsquare/react-native-bridge/babel'],
};
The Babel plugin requires @babel/plugin-transform-react-display-name to include component names in events. This is typically provided by React Native's default preset.
Monorepo setup: If your project uses a monorepo structure (Yarn Workspaces, pnpm, Lerna, Nx, Turborepo, etc.), make sure to add the plugin to all relevant babel.config.js files in your project, including those in any packages that contain React Native code.
Enable Autocapture
import { CSQ, StartConfig } from '@contentsquare/react-native-bridge';
CSQ.start(StartConfig.withEnvironmentId('your-env-id', {
enableRNAutocapture: true // Enable autocapture
}));
Navigation Library Compatibility
The React Native Bridge's autocapture feature is compatible with the following navigation libraries:
- React Navigation: 5.0 and later
- React Native Navigation: 7.0 and later
What Gets Autocaptured
Autocapture automatically tracks:
- Touch events (tap, long press, swipe)
- Change events (text input, switches, pickers, sliders)
- Component interactions with display names
- Screen navigation (when enabled)
Combined with Experience Analytics
When using both Experience Analytics and Product Analytics, pass PA options via StartConfig.withDataSourceId:
CSQ.start(StartConfig.withDataSourceId('YOUR_DATA_SOURCE_ID', {
enableRNAutocapture: true,
disablePageviewAutocapture: true, // Let Experience Analytics handle screens
}));
Source Maps and Crash Symbolication
For readable JavaScript crash stack traces in production, configure source map upload:
Quick Setup
-
Install the Contentsquare CLI:
yarn add @contentsquare/react-native-cli --dev -
Create
contentsquare-cli.jsonat project root with API credentials and source map paths -
Configure build scripts:
- Android: Add
apply from: "../../node_modules/@contentsquare/react-native-cli/scripts/contentsquare.gradle"toapp/build.gradle - iOS: Add Contentsquare CLI script to Xcode build phase
- Android: Add
-
Add to
.gitignore:contentsquare-cli.json contentsquare-sourcemap-info.json
See Debugging and Tooling for complete setup instructions and troubleshooting.
Platform Support
- Android: Full support
- iOS: Full support (check iOS compatibility requirements)
- React Native: >= 0.71.0 (version 6.2.0 supports all current and future versions from 0.71.0 onwards)
- Expo: Requires EAS (Expo Application Services) - see Compatibility Guide
- Not Supported: Web, Windows, macOS, Expo Go
Important: Hybrid applications (React Native components in native apps) are not officially supported. For mixed codebases, use native SDKs for native screens and the CSQ bridge for React Native screens.
See Compatibility Guide for detailed version matrices, Expo configuration, and known limitations.
Reference Documentation
- API Reference - Complete API documentation
- Common Patterns - Code examples and recipes
- Screen Tracking - React Navigation integration patterns
- Session Replay - Advanced masking strategies
- Debugging and Tooling - Autocapture setup, source maps, CLI tools
- WebView Tracking - WebView integration and session stitching
- In-App Features - Debugging tools, logs, screenshot capture
- Compatibility - React Native versions, Expo, known limitations
- Migration Guide - Legacy API migration
- React Native SDK docs
- Android SDK docs
- iOS compatibility