Experience SDK
Table of contents
Overview
We provide SDKs and plugins for modern web frameworks to use Contentful Analytics. Our SDKs include first-class TypeScript support with exported type definitions for a smooth development experience in TypeScript projects.
The SDKs provide abstractions that communicate with the APIs that power Contentful Analytics by:
- Creating
page,track, andidentifyevents and sending them to the Experience API - Providing built-in capabilities to handle errors, retries and queuing
- Enabling caching of the profile client-side via
localStorage.
Composed JavaScript SDKs
Our SDKs have three levels of abstraction. At the lowest-level, the Shared SDK @ninetailed/experience.js-shared creates an API Client that communicates with the Experience API's endpoints. The data objects, types, and methods it provides are applicable across all SDKs.
The JavaScript SDK @ninetailed/experience.js is based on the Shared SDK. It creates an instance that stores and updates a profile in response to events.
Front-end SDKs
The Shared and JavaScript SDKs can be used to integrate Contentful Analytics with arbitrary front-ends that run JavaScript. For fast integration with popular front-end frameworks, we provide SDKs for:
- React (@ninetailed/experience.js-react)
- Next.js (@ninetailed/experience.js-next)
- Gatsby (experience.js-gatsby).
Server-side SDK
The Node.js SDK (@ninetailed/experience.js-node) is provided for integrating Contentful Analytics in server or edge runtimes.
Install the SDK
Dependency installation
npm install @ninetailed/experience.js-react
# OR
yarn add @ninetailed/experience.js-react
npm install @ninetailed/experience.js-next
# OR
yarn add @ninetailed/experience.js-next
npm install @ninetailed/experience.js-gatsby
# OR
yarn add @ninetailed/experience.js-gatsby
npm install @ninetailed/experience.js
# OR
yarn add @ninetailed/experience.js
Create an instance
At its core, the Experience SDK defines a Ninetailed class instance based on your input configuration. That instance then:
- Keeps track of the current profile and provides a hook into profile changes.
- Exposes declarative methods to interact with the Experience API.
- Provides extensibility through plugins.
When working with the React, Next.js or Gatsby SDK, an instance will be created and made available to your application globally through a React context provider.

Add the <NinetailedProvider> component to the top level of the application, so that the profile can be consumed from any child component.
import React from 'react';
import MyAppCode from '../MyAppCode.jsx';
import { NinetailedProvider } from '@ninetailed/experience.js-react';
export const App = () => {
return (
<div id="my-app">
<NinetailedProvider
// REQUIRED. An API key uniquely identifying your Ninetailed account.
clientId="NINETAILED_API_KEY"
// === ALL OF THE FOLLOWING PROPS ARE OPTIONAL ===
// === DEFAULT VALUES ARE SHOWN ===
// Your Ninetailed environment, typically "main" or "development"
environment="main"
// Add any plugin instances
plugins=[]
// Specify an amount of time (ms) that an component must
// be present in the viewport to register a component view
componentViewTrackingThreshold={2000}
// Specify a maximum amount of time (ms) to wait for an Experience API response before falling back to baseline content
requestTimeout={5000}
// Specify a locale to localize profile location information
locale="en-US"
// Specify an alternative Experience API base URL
url="https://experience.ninetailed.co"
// Set to true ONLY if using an unindexed CMS
useSDKEvaluation=true
>
<MyAppCode />
</NinetailedProvider>
</div>
);
}
We recommend adding the <NinetailedProvider> component to the top level of the application. This way, the profile can be consumed from any child component.
The Next.js <NinetailedProvider> additionally hooks into the Next page router and automatically calls ninetailed.page() on every route change. Calling this method again will lead to duplicated events.
import { NinetailedProvider } from '@ninetailed/experience.js-next';
export const App = ({component, pageProps}) => {
return (
<div id="my-app">
<NinetailedProvider
// REQUIRED. An API key uniquely identifying your Ninetailed account.
clientId="NINETAILED_API_KEY"
// === ALL OF THE FOLLOWING PROPS ARE OPTIONAL ===
// === DEFAULT VALUES ARE SHOWN ===
// Your Ninetailed environment, "main" or "development"
environment="main"
// Add any plugin instances
plugins=[]
// Specify an amount of time (ms) that an <Experience /> component must
// be present in the viewport to register a component view
componentViewTrackingThreshold={2000}
// Specify a maximum amount of time (ms) to wait for an Experience API response before falling back to baseline content
requestTimeout={5000}
// Specify a locale to localize profile location information
locale="en-US"
// Specify an alternative Experience API base URL
url="https://experience.ninetailed.co"
// Set to true ONLY if using an unindexed CMS
useSDKEvaluation=true
>
<Component {...pageProps} />
</NinetailedProvider>
</div>
);
}
Add the plugin to your plugins array in your gatsby-config.js|ts file. When using the Gatsby JS plugin you don't have to configure the NinetailedProvider as described for the React and Next.js SDKs.
The plugin automatically calls ninetailed.page() on every route change. Calling this method again will lead to duplicated events.
... // Your Other Gatsby configuration
plugins: [
... // Your existing Gatsby plugins
{
resolve: `@ninetailed/experience.js-gatsby`,
options: {
// REQUIRED. An API key uniquely identifying your Ninetailed account.
clientId: "NINETAILED_API_KEY",
// === ALL OF FOLLOWING PROPERTIES ARE OPTIONAL ===
// === DEFAULT VALUES ARE SHOWN ===
// Your Ninetailed environment, "main" or "development"
environment: "main",
// Add any plugin instances
ninetailedPlugins: [],
// Specify an amount of time (ms) that a component must
// be present in the viewport to register a component view
componentViewTrackingThreshold: 2000,
// Specify a maximum amount of time (ms) to wait for an
// Experience API response before falling back to baseline content
requestTimeout: 5000,
// Specify a locale to localize profile location information
locale: "en-US",
// Specify an alternative Experience API base URL
url: "https://experience.ninetailed.co",
// Set to true ONLY if using an unindexed CMS
useSDKEvaluation: true
}
}
]
import { Ninetailed } from '@ninetailed/experience.js';
export const ninetailed = new Ninetailed(
{
// REQUIRED. An API key uniquely identifying your Ninetailed account.
clientId: "NINETAILED_API_KEY",
// OPTIONAL. Your Ninetailed environment, "main" or "development"
environment: "main" // Default
},
// === THE FOLLOWING ARGUMENT AND ALL OF ITS PROPERTIES ARE OPTIONAL ===
// === DEFAULT VALUES ARE SHOWN ===
{
// Add any plugin instances
plugins: [],
// Specify an amount of time (ms) that a component must
// be present in the viewport to register a component view
componentViewTrackingThreshold: 2000,
// Specify a maximum amount of time (ms) to wait for an Experience API response before falling back to baseline content
requestTimeout: 5000,
// Specify a locale to localize profile location information
locale: "en-US",
// Specify an alternative Experience API base URL
url: "https://experience.ninetailed.co"
// Set to true ONLY if using an unindexed CMS
useSDKEvaluation: true
}
);
Browser instance access
Installing the Experience SDK exposes several properties and methods on a window.ninetailed instance for testing and debugging.
| Properties and methods | Description |
|---|---|
page(), track(), and identify() |
The core tracking methods. |
debug(arg: boolean) |
Turn on debug mode, which will output additional information to the console. |
plugins |
Access the methods of any plugins attached to the instance. |
profile |
Output the current profile state. |
reset() |
Discard the current profile. |
For a full list of instance properties and methods, see the JavaScript SDK documentation.
Send events
Contentful Analytics uses profiles to identify users. A profile is created and updated by sending three types of events about that profile to the Experience API: page, track, and identify. We recommend using the methods provided by any of the Experience SDKs for sending events.
page events
type Page = (properties?: Object) => Promise<void>;
A page event represents a user viewing a page. The SDKs provide a context object that captures important parameters of the page, including the referrer, url, path, and user-agent, which are sent to the Experience API.
Typical usecases will call the page method without arguments. However, you may optionally specify any properties you want to send alongside the page view event. This can be useful for creating Audiences - for example, if the category of blog posts is not contained in the URL, yet you'd like to create an Audience that has viewed blog posts of a certain category a certain number of times. In this case, you can pass the category along in the argument:
page({'category': 'YOUR_BLOG_CATEGORY'})
- The Gatsby SDK automatically sends a
pageevent on every route change. - The Next.js SDK also sends
pageevents automatically on page changes, but only if you're using Page Router. If you're using App Router, you have to set up the logic for sending the event. Here is an example where the event is sent in theTrackPagescomponent for App Router. - If your application uses the React, JavaScript or Node SDK, you have to manually implement a
pagecall on every route change.
track events
type Track = (event: string, properties?: Object) => Promise<void>;
A track event logs user actions, like signup or registered_for_newsletter.
It can be arbitrarily named and accepts a properties argument that enriches the event with additional information.
For example, you might include the SKU and quantity of items on a track event called "add_to_cart".
track('add_to_cart', {sku: '9T41L', quantity: 1})
Contentful Analytics uses track events to calculate conversions and the importance measure.
You have to configure a metric for track events for this purpose.
identify events
type Identify = (uid: string, traits?: Traits) => Promise<void>;
An identify event allows adding custom information, called traits, to a profile.
Traits are arbitrary attributes in JSON form. They allow you to customize the data that segments profiles into audiences.
For example, a user's responses from a sign up form, a list of recently viewed products, or data from any upstream source, like a CRM or CDP, may be traits.
identify('', { favoriteColor: "red" })
identify events allow setting a custom alias for a profile. That way, IDs stored within, for example, an analytics system, customer data platform, or e-commerce platform can be used to identify that profile.
identify('customer12345')
An aliasing profile is reinstating on a different device or browser by calling identify again, which merges the latest activity of the current profile with the profile at that alias.
To reidentify users across sessions, we recommend calling identify after each successful authentication.
identify is powerful but should be used with caution. In particular, you should consider what privacy legislation your application needs to abide by, and whether that affects what data should not be stored as traits. For example, you probably would not want to store contact information, such as email addresses or phone numbers as traits, or use them as aliases.
component events
A component event records every time a user views a Contentful content entry on a website. It includes contextual details such as the entry ID, timestamp, device type and environment. component events are automatically sent by the SDK.
Component events:
- Track the total number of views for each component.
- Attribute conversions to specific components.
- Analyze the impact of content at the component level.
- Support Contentful Personalization by linking views to specific experiences or variants.
Access event methods
The useNinetailed hook provides the tracking methods page, track, and identify. These will call the Experience API using the configuration parameters supplied to a <NinetailedProvider>.
import React from 'react';
import { useNinetailed } from '@ninetailed/experience.js-react';
// or import { useNinetailed } from '@ninetailed/experience.js-next';
// or import { useNinetailed } from '@ninetailed/experience.js-gatsby';
export const myComponent = () => {
const { page, track, identify } = useNinetailed();
page();
identify('anAlias', {someTrait: "value"});
return (
<button
type="button"
onClick={() => { track('add_to_cart'); }}
>
Add to Cart
</button>
);
}
The page, track, and identify methods are available directly on a Ninetailed class instance.
import { Ninetailed } from '@ninetailed/experience.js';
const ninetailed = new Ninetailed({ clientId: "NINETAILED_API_KEY"})
ninetailed.page();
ninetailed.track('myEvent')
ninetailed.identify('alias', {traitName: "traitValue"})
Render Analytics Entries
When using one of the React SDKs, Contentful entries must be rendered as children of an <EntryAnalytics /> component, or an <Experience /> component for Contentful Analytics to work correctly.
- Render entries as children of an
<EntryAnalytics />component if you use Contentful Analytics as a standalone product. - Render entries as children of an
<Experience />component if you use Contentful Analytics and Contentful Personalization. This will require additional setup work as described in the Personalization Experience SDK documentation.
The <EntryAnalytics /> component
The React-based SDKs provide an <EntryAnalytics /> component for rendering Contentful Analytics entries.
7.17.6.
The <EntryAnalytics> component functions wraps your existing React component. It automatically detects the properties needed from the wrapped component.
| Prop | Required | Description |
|---|---|---|
{...props} |
Yes | All props the component prop requires to render the entry. |
id |
Yes | The Contentful entry ID of the entry. |
component |
Yes | The React component that your entry will use to render. This can either be a regular React component, or a component that uses React's forwardRef. |
Example use
// or '@ninetailed/experience.js-next', '@ninetailed/experience.js-gatsby'
import { EntryAnalytics } from '@ninetailed/experience.js-react';
// This function is assumed to return a single entry and all its supporting data.
import { getCmsEntry } from '../api/yourEntryGetter'
import { YourComponent } from './YourComponent'
export const YourAnalytics = (contentfulEntry) => {
const entry = getCmsEntry(contentfulEntry);
return (
<EntryAnalytics
{...entry}
id={entry.sys.id}
component={YourComponent}
/>);
};