Dev guideAPI Reference
Dev guideAPI ReferenceUser GuideGitHubNuGetDev CommunitySubmit a ticketLog In
GitHubNuGetDev CommunitySubmit a ticket

Initialize the JavaScript (Browser) SDK

How to initialize the Optimizely Feature Experimentation JavaScript (Browser) SDK in your application.

Use the createInstance method to initialize the JavaScript (Browser) SDK and instantiate an instance of the Optimizely client class that exposes API methods like Decide methods. Each client corresponds to the datafile representing the state of a project for a certain environment.

Version

SDK v5.0.0

Description

The createInstance method accepts a configuration object to configure Optimizely Feature Experimentation.

Some parameters are optional because the SDK provides a default implementation, but you may want to override these for your production environments. For example, you may want override these to set up an error handler and logger to catch issues, an event dispatcher to manage network calls, and a User Profile Service to ensure sticky bucketing.

Parameters

The table below lists the required and optional properties of the config object:

ParameterTypeDescription
datafile
optional
stringThe JSON string representing the project. At least one of sdkKey or datafile must be provided.
sdkKey
optional
stringThe key associated with an environment in the project. At least one of sdkKey or datafile must be provided.
eventDispatcher
optional
objectAn event dispatcher to manage network calls. An object with a dispatchEvent method.
logger
optional
objectA logger implementation to log messages. An object with a log method.
errorHandler
optional
objectAn error handler object to handle errors. An object with a handleError method
userProfileService
optional
objectA user profile service. An object with lookup and save methods.
jsonSchemaValidator
optional
objectTo perform JSON schema validation on datafiles, import the validator from '@optimizely/optimizely-sdk/dist/optimizely.json_shema_validator.min.js', and pass it as this initialization option. Skipping JSON schema validation enhances performance during initialization.
datafileOptions
optional
objectObject with configuration for automatic datafile management. Can have autoUpdate (Boolean), urlTemplate (string), and updateInterval (number) properties.
access_token
optional
stringServer-side (only) Optimizely Feature Experimentation SDKs can use an access token (with sdk key) to fetch the datafile from an authenticated endpoint. Find your datafile access token in the Optimizely app at Settings>Environments. Select your secure environment, and copy the Datafile access token.
defaultDecideOptions
optional
ArrayArray of OptimizelyDecideOption enums. When the Optimizely client is constructed with this parameter, it sets default decide options which are applied to all the Decide calls made during the lifetime of the Optimizely client. Additionally, you can pass options to individual Decide methods (does not overrides defaults).
For example code, see OptimizelyDecideOption.
odpOptions
optional
OptimizelySdkSettingsContains the logic supporting Real-Time Segments for Feature Experimentation-related features, including audience segments, sending events, and VUID management.

The JavaScript (Browser) SDK automatically enables the Real-Time Segments for Feature Experimentation methods. But, the methods do nothing unless you have enabled and configured Real-Time Segments for Feature Experimentation.

To optionally disable Real-Time Segments for Feature Experimentation, see OdpManager.

Returns

Instantiates an instance of the Optimizely class.

Examples

In the JavaScript SDK, you can provide a sdkKey or datafile or both.

  • When initializing with just the SDK key – The datafile will be automatically downloaded.
  • When initializing with just the datafile – The SDK will use the given datafile.
  • When initializing with both the SDK key and datafile – The SDK will use the given datafile to start, then download the latest version of the datafile in the background.

Instantiate using datafile

First, get a copy of the datafile from the Optimizely server. Then, instantiate the client.

// minimal client
import { createInstance } from '@optimizely/optimizely-sdk';

// Replace <YOUR_SDK_KEY> to get the datafile
const sdkKey = '<YOUR_SDK_KEY>';
const DATAFILE_URL = `https://cdn.optimizely.com/datafiles/${sdkKey}.json`;
const response = await fetch(DATAFILE_URL);
const datafile = await response.json();

const optimizely = createInstance({
  datafile
});

if (!optimizely) {
  // there was an error creating the instance, handle error
} else {
  // Use optimizelyClient to run experiments
}

<!--step 1: add a <script> tag in your <head> tag: 
 this adds the datafile on the window variable window.optimizelyDatafile-->

<!-- Replace <YOUR_SDK_KEY> to get the datafile -->

<script src="https://cdn.optimizely.com/datafiles/<YOUR_SDK_KEY>.json/tag.js"></script>

<!--step 2. after the <body> tag, add a new <script> tag to instantiate an Optimizely client  from the datafile:-->

<script>
    var optimizely = window.optimizelySdk.createInstance({
        datafile: window.optimizelyDatafile,
    });
</script>

If you do not pass in an SDK key, the Optimizely Client will not automatically sync newer datafile versions. Any time you retrieve an updated datafile, just re-instantiate the same client.

For simple applications, all you need to provide to instantiate a client is a datafile specifying the project configuration for a given environment. For most advanced implementations, you will want to customize the logger or error handler for your specific requirements.

Instantiate using SDK Key

To instantiate using SDK Key, obtain the SDK Key from your project's settings page, then pass sdkKey as a string property in the options object you pass to the createInstance method.

import { createInstance } from '@optimizely/optimizely-sdk';
const optimizely = createInstance({
  sdkKey: '<YOUR_SDK_KEY>', // Provide the sdkKey of your desired environment here
});

When you provide the sdkKey, the SDK instance asynchronously downloads the datafile associated with that sdkKey. When the download completes, the SDK instance updates itself to use the downloaded datafile. You can use the onReady promise method to wait for the datafile to be downloaded before using the instance.

import { createInstance } from '@optimizely/optimizely-sdk';
const optimizely = createInstance({
  sdkKey: '<YOUR_SDK_KEY>', // Provide the sdkKey of your desired environment here
});

if (!optimizely) {
  // there was an error creating the instance, handle error
} else {
  // Use optimizelyClient
  optimizely.onReady().then(({ success, reason }) => {
    if (success) {
      // optimizelyClientInstance is ready to use, with datafile downloaded from the
      // Optimizely CDN
    } else {
      // this optimizelyClientInstance cannot be used
      console.log(`client initialization unsuccessful, reason: ${reason}`);
    }
  });
}

For simple applications, all you need to provide to instantiate a client is a datafile specifying the project configuration for a given environment. For most advanced implementations, you will want to customize the logger or error handler for your specific requirements.

Notes

Customize datafile management behavior

To customize datafile management behavior, provide a datafileOptions object property inside the options object passed to createInstance. The table lists the supported customizable options.

OptionTypeDescription
autoUpdatebooleanWhen true, and sdkKey was provided in createInstance options, automatic updates are enabled on this instance. The default value is false.
updateIntervalnumberWhen automatic updates are enabled, this controls the update interval. The unit is milliseconds. The minimum allowed value is 1000 (1 second). The default value is 300000 milliseconds (5 minutes).
urlTemplatestringA format string used to build the URL from which the SDK will request datafiles. Instances of %s will be replaced with the sdkKey. When not provided, the SDK will request datafiles from the Optimizely CDN.

The following example shows how to customize datafile management behavior:

import { createInstance } from '@optimizely/optimizely-sdk';

const optimizely = createInstance({
  sdkKey: '<YOUR_SDK_KEY>',
  datafileOptions: {
    autoUpdate: true,
    updateInterval: 600000, // 10 minutes in milliseconds
    urlTemplate: 'http://localhost:5000/datafiles/%s.json',
  },
});

onReady details

Use the onReady method to wait until the download is complete and the SDK is ready to use.

The onReady method returns a Promise representing the initialization process.
onReady accepts an optional timeout argument (defined in milliseconds) that controls the maximum duration that the returned Promise will remain in the pending state. If timeout is not provided, it defaults to 30 seconds.

import { createInstance } from '@optimizely/optimizely-sdk';
let optimizely = createInstance({
  sdkKey: '<YOUR_SDK_KEY>',
});

if (!optimizely) {
  // there was an error creating the instance, handle error
} else {
  // Use optimizelyClient
  optimizely.onReady().then(result => {
    // Returned Promise is fulfilled with a result object
    console.log(result.success); // true if the instance fetched a datafile and is now ready to use
    console.log(result.reason); // If success is false, reason contains an error message
  });

  // Provide a timeout in milliseconds - promise will resolve if the datafile still is not available after the timeout
  optimizely.onReady({ timeout: 5000 }).then(result => {
    // Returned Promise is fulfilled with a result object
    console.log(result.success); // true if the instance fetched a datafile and is now ready to use
    console.log(result.reason); // If success is false, reason contains an error message
  });
};

The Promise returned from the onReady method is fulfilled with a result object containing a boolean success property.

When the success property is true, the instance is ready to use with a valid datafile. When the success property is false, there is also a reason string property describing the failure. Failure can be caused by expiration of the timeout, network error, unsuccessful HTTP response, datafile validation error, or the instance's close method being called.

Set a fallback datafile

If you provide an sdkKey and a static fallback datafile for initialization, the SDK uses the fallback datafile immediately if it is valid while simultaneously downloading the datafile associated with the sdkKey. After the download completes, if the downloaded datafile is valid and has a more recent revision than the fallback datafile, the SDK updates the instance to use the downloaded datafile.

import { createInstance } from '@optimizely/optimizely-sdk';

const datafile = '{"version": "4", "rollouts": [], "typedAudiences": [], "anonymizeIP": false, "projectId": "12345", "variables": [], "featureFlags": [], "experiments": [], "audiences": [], "groups": [], "attributes": [], "botFiltering": false, "accountId": "12345", "events": [], "revision": "1"}'; 
const optimizely = createInstance({
  sdkKey: '<Your_SDK_Key>',
  datafile,
});
// optimizelyClient can be used immediately with the given datafile, but
// will download the latest datafile and update itself

Dispose of the client

For effective resource management with the Optimizely JavaScript (Browser) SDK, you must properly close the Optimizely client instance when it is no longer needed. This is done by calling optimizely.close().

The .close() method ensures that the processes and queues associated with the instance are properly released. This is essential for preventing memory leaks and ensuring that the application runs efficiently, especially in environments where resources are limited or in applications that create and dispose of many instances over their lifecycle.

See Close Optimizely Feature Experimentation JavaScript (Browser) SDK on application exit.

OdpManager

OdpManager contains the logic supporting Real-Time Segments for Feature Experimentation-related features, including audience segments, ODP events, and VUID management.

The JavaScript (Browser) SDK automatically enables the Real-Time Segments for Feature Experimentation methods. But, the methods do nothing unless you have enabled and configured Real-Time Segments for Feature Experimentation.

If necessary, to disable Real-Time Segments for Feature Experimentation altogether, set disabled: true. See the following sample code.

Initialize the JavaScript (Browser) SDK with Real-Time Segments for Feature Experimentation disabled:

import { createInstance } from '@optimizely/optimizely-sdk';

const optimizely = createInstance({
  sdkKey: '<YOUR_SDK_KEY>',
  odpOptions: {
    disabled: true,
  },
});

The following settings are optionally configurable when the JavaScript (Browser) SDK is initialized:

  • ODP SegmentsCache sizesegmentsCacheSize
    • Default – 100
    • Set to 0 to disable caching.
  • ODP SegmentsCache timeout (in seconds) – segmentsCacheTimeout
    • Default – 600 secs (10 minutes)
    • Set to 0 to disable timeout (never expires).
  • ODP enabledisabled
    • Default – false (enabled)
    • When disabled, the JavaScript (Browser) SDK will disable ODP-related features. The JavaScript (Browesr) SDK still creates and manages VUID regardless of this flag and supports VUID-based decisions. See anonymous users.
    • The JavaScript (Browser) SDK returns or logs an odpNotEnabled error when ODP is disabled and its features are requested.

Initialize the JavaScript (Browser) SDK with Real-Time Segments for Feature Experimentation and custom settings:

import { createInstance } from '@optimizely/optimizely-sdk';

const optimizely = createInstance({
  sdkKey: '<YOUR_SDK_KEY>',
  odpOptions: {
    // disabled: false,  // change to true to disable
    eventApiTimeout: 1000,
    eventQueueSize: 1,
    // eventRequestHandler: new BrowserRequestHandler(),
    // eventManager: new OdpEventManager(),
    segmentsApiTimeout: 1000,
    segmentsCacheSize: 10,
    segmentsCacheTimeout: 1000,
    // segmentsCache: new ICache<string, string[]>(),
    // segmentsRequestHandler: new BrowserRequestHandler(),
    // segmentManager: new OdpSegmentManager(),
    
  },
});

📘

Note

Unlike other Feature Experimentation SDKs for Real-Time Segments for Feature Experimentation, the JavaScript (Browser) cannot batch events. Because of that limitation, you cannot update the following:

  • eventBatchSize – Will always be set to 1.
  • eventFlushInterval –Wiill always be set to 0.

Call getVuid() after the client successfully initializes

// After successfully initializing the JavaScript (Browser) SDK...
const deviceVuid = optimizely.getVuid();  
console.log('Device VUID: ${deviceVuid}');

For information, see VUIDs and client-side SDKs.

Customize OdpManager

Using the parameters in odpOptions, you can customize the behavior of the ODP event and segment managers, even going as far as being able to replace them with your own implementations if desired.

Example odpOptions with Custom Event Manager

odpOptions: {
  // …
  eventManager: new OdpEventManager({
    odpConfig, // new OdpConfig()
    apiManager, // new OdpEventApiManager()
    logger, // new LogHandler()
    clientEngine: 'javascript-sdk',
    clientVersion: '5.0.0',
    batchSize: 10, // Cannot change in JavaScript (Browser) environment.
    flushInterval: 2000, // Cannot change in JavaScript (Browser) environment.
  }),
},

Customize OdpEventApiManager

customizing OdpEventApiManager requires the user to provide an object with the following methods:

export interface IOdpEventApiManager {
  sendEvents(apiKey: string, apiHost: string, events: OdpEvent[]): Promise<boolean>;
}

Example odpOptions w/Custom Segments Manager

odpOptions: {  
  // ...
  segmentManager: new OdpSegmentManager(  
    odpConfig, // new OdpConfig()  
    new BrowserLRUCache<string, string[]>({  
      maxSize: 2,  
      timeout: 4000,  
    }), // new ICache<string, string[]>()  
    segmentApiManager, // new IOdpSegmentApiManager()  
  ),  
},

Customize the OdpSegmentApiManager:

export interface IOdpSegmentApiManager {  
  fetchSegments(  
    apiKey: string,  
    apiHost: string,  
    userKey: string,  
    userValue: string,  
    segmentsToCheck: string[]  
  ): Promise<string[] | null>;  
}

Custom cache

You can provide a custom cache, which is used to store the fetchQualifiedSegments results. To provide a custom cache, you should implement the following interface:

Segments Cache Interface

export interface ICache<K, V> {
  lookup(key: K): V | null;
  save({ key, value }: { key: K; value: V }): void;
  reset(): void;
}

Custom Segments Cache Implementation example from LRUCache implementation

export class LRUCache<K, V> implements ICache<K, V> {
  private _map: Map<K, CacheElement<V>> = new Map();
  private _maxSize; // Defines maximum size of _map
  private _timeout; // Milliseconds each entry has before it becomes stale

  get map(): Map<K, CacheElement<V>> {
    return this._map;
  }

  get maxSize(): number {
    return this._maxSize;
  }

  get timeout(): number {
    return this._timeout;
  }

  constructor({ maxSize, timeout }: LRUCacheConfig) {
    const logger = getLogger();

    logger.debug(`Provisioning cache with maxSize of ${maxSize}`);
    logger.debug(`Provisioning cache with timeout of ${timeout}`);

    this._maxSize = maxSize;
    this._timeout = timeout;
  }

  public lookup(key: K): V | null {
    if (this._maxSize <= 0) {
      return null;
    }

    const element: CacheElement<V> | undefined = this._map.get(key);

    if (!element) return null;

    if (element.is_stale(this._timeout)) {
      this._map.delete(key);
      return null;
    }

    this._map.delete(key);
    this._map.set(key, element);

    return element.value;
  }

  public save({ key, value }: { key: K; value: V }): void {
    if (this._maxSize <= 0) return;

    const element: CacheElement<V> | undefined = this._map.get(key);
    if (element) this._map.delete(key);
    this._map.set(key, new CacheElement(value));

    if (this._map.size > this._maxSize) {
      const firstMapEntryKey = this._map.keys().next().value;
      this._map.delete(firstMapEntryKey);
    }
  }

  public reset(): void {
    if (this._maxSize <= 0) return;

    this._map.clear();
  }

  public peek(key: K): V | null {
    if (this._maxSize <= 0) return null;

    const element: CacheElement<V> | undefined = this._map.get(key);

    return element?.value ?? null;
  }
}

An additional dependency was added to the Optimizely client’s readyPromise promise resolution dependency array which resolves based on the success of initializing the VuidManager using localStorage.

Use authenticated datafile in a secure environment

You can fetch the Optimizely datafile from an authenticated endpoint using a server-side (only) Optimizely Feature Experimentation SDK.

To use an authenticated datafile, download your Optimizely environment's access token from the Optimizely app at Settings>Environments. Select your secure environment, and copy the Datafile access token. The example below shows how to initialize the Optimizely client using an access token and sdk_key, enabling the client to fetch the authenticated datafile and complete initialization.

import { createInstance } from '@optimizely/optimizely-sdk';

// fetch the datafile from an authenticated endpoint
const optimizely = createInstance({
  sdkKey: '<YOUR_SDK_KEY>',
  datafileOptions: {
    datafileAccessToken: '<YOUR_DATAFILE_ACCESS_TOKEN>',
  },
});

Source files

The source code files containing the implementation for the JavaScript SDK are available on GitHub.