Skip to main content

JavaScript Client SDK

Working with the SDK

Checking a Feature Flag/Gate

Now that your SDK is initialized, let's check a Feature Gate. Feature Gates can be used to create logic branches in code that can be rolled out to different users from the Statsig Console. Gates are always CLOSED or OFF (think return false;) by default.

...

Reading a Dynamic Config

Feature Gates can be very useful for simple on/off switches, with optional but advanced user targeting. However, if you want to be able send a different set of values (strings, numbers, and etc.) to your clients based on specific user attributes, e.g. country, Dynamic Configs can help you with that. The API is very similar to Feature Gates, but you get an entire json object you can configure on the server and you can fetch typed parameters from it. For example:

...

See Typed Getters to learn more about accessing values.

Getting an Layer/Experiment

Then we have Layers/Experiments, which you can use to run A/B/n experiments. We offer two APIs, but we recommend the use of layers to enable quicker iterations with parameter reuse.

...

See Typed Getters to learn more about accessing values.

Logging an Event

Now that you have a Feature Gate or an Experiment set up, you may want to track some custom events and see how your new features or different experiment groups affect these events. This is super easy with Statsig - simply call the Log Event API for the event, and you can additionally provide some value and/or an object of metadata to be logged together with the event:

...

Learn more about identifying users, group analytics, and best practices for logging events in the logging events guide.

Typed Getters

The Layer, Experiment and DynamicConfig objects all support a "typed" get method. This method can help avoid issues where a value is not the type you expect.

For example, imagine we have a Dynamic Config with a single number value called "my_value":

// { "my_value": 1 }

const myDynamicConfig = myStatsigClient.getDynamicConfig("a_config");

const myFallbackValue = myDynamicConfig.get("my_value", "fallback"); // returns: "fallback"
const myTypedValue = myDynamicConfig.get("my_value", 0); // returns: 1
const myRawValue = myDynamicConfig.get("my_value"); // returns: 1

Because "my_value" points to a number but in the myFallbackValue case, we are calling .get with a string fallback value, the fallback is being returned.

In the myTypedValue case, we are passing a number fallback value, and since "my_value" is also a number, the actual value of 1 is returned.

If typing is not important to you, the fallback argument can be omitted, and the SDK will simply return the value. This is highlighted in the myRawValue case.

Evaluation Details

When you receive a value, it may be useful to know how the SDK came to that result. For this purpose, on every Statsig type there exists a way to check the EvaluationDetails object.

This object contains the following fields:

  • Reason: This is a string containing the source as well as whether or not the specific config was found.
    • Network:Recognized means the SDK has the latest values and found an entry for the config.
    • Cache:Unrecognized means we are working with cached values, are could not find the given config.
    • For the full list of possible combinations, see the page on Debugging.
  • lcut: Last Config Update Time - This is the unix timestamp for when any configuration in your project changed.
    • lcut works as a version number for you project. If it changes, values are deemed out of date and possibly need to be refetched.
  • receivedAt: This is the unix timestamp of when the SDK received these values. This can be useful in knowing how old your cache is.
...

Code Examples

Prefer seeing it in practice? Included in the open source repository are some Code Examples. View these for common use cases for the SDK.

Parameter Stores

Parameter Stores hold a set of parameters for your mobile app. These parameters can be remapped between Statsig entities (Feature Gates, Experiments, and Layers), so you can decouple your code from the configuration in Statsig.

You can read more about the concept here.

Getting a Parameter Store

To fetch a set of parameters, use the following api:

const homepageStore = myStatsigClient.getParameterStore("homepage");

Getting a parameter

You can then access parameters like this:

const title = homepageStore.get(
"title", // parameter name
"Welcome", // default value
);

const showUpsell = homepageStore.get(
"upsell_upgrade_now",
false,
);

Statsig User

You should provide a StatsigUser object whenever possible when initializing the SDK, passing as much information as possible in order to take advantage of advanced gate and config conditions (like country or OS/browser level checks).

Most of the time, the userID field is needed in order to provide a consistent experience for a given user (see logged-out experiments to understand how to correctly run experiments for logged-out users).

If the user is logged out at the SDK init time, you can leave the `userID` out for now, and we will use a stable device ID that we create and store in the local storage for targeting purposes.

Besides userID, we also have email, ip, userAgent, country, locale and appVersion as top-level fields on StatsigUser. In addition, you can pass any key-value pairs in an object/dictionary to the custom field and be able to create targeting based on them.

Once the user logs in or has an update/changed, make sure to call `updateUser` with the updated `userID` and/or any other updated user attributes:

Private Attributes

Have sensitive user PII data that should not be logged? No problem, we have a solution for it! On the StatsigUser object we also have a field called privateAttributes, which is a simple object/dictionary that you can use to set private user attributes. Any attribute set in privateAttributes will only be used for evaluation/targeting, and removed from any logs before they are sent to Statsig server.

For example, if you have feature gates that should only pass for users with emails ending in "@statsig.com", but do not want to log your users' email addresses to Statsig, you can simply add the key-value pair { email: "my_user@statsig.com" } to privateAttributes on the user and that's it!

Updating Users

At some point, you will probably need to change the StatsigUser being used by the Statsig client. To do this you can call updateUserSync or updateUserAsync.

In advanced use cases, you may want to Prefetch or Bootstrap (Provide) values for the updating of a user. See Using EvaluationsDataAdapter to learn how this can be achieved.

...

Prefetching Users

In some cases, it can be useful to prefetch values for a given user, such that switching to that user later can be done synchronously.

To do this, you would call prefetchData and then later updateUserSync.

...

Statsig Options

disableLogging boolean

Prevents sending any events over the network.

disableStorage boolean

Prevents writing anything to storage.

Note: caching will not work if storage is disabled

networkConfig object

Allows for fine grained control over which api or urls are hit for specific Statsig network requests

See NetworkConfig object

api string

The API to use for all SDK network requests. You should not need to override this unless you have a custom API that implements the Statsig endpoints.

logEventUrl string

The URL used to flush queued events via a POST request. Takes precedence over NetworkConfig.api.

default: https://prodregistryv2.org/v1/rgstr

networkTimeoutMs number

The maximum amount of time (in milliseconds) that any network request can take before timing out.

default: 10,000 ms (10 seconds)

preventAllNetworkTraffic boolean

Intended for testing purposes. Prevents any network requests being made.

networkOverrideFunc function

Overrides the default networking layer used by the Statsig client. By default, the client use fetch, but overriding this you could use axios or raw XMLHttpRequest

default: fetch

initializeUrl string

The URL used to fetch the latest evaluations for a given user. Takes precedence over NetworkConfig.api.

default: https://featureassets.org/v1/initialize

environment StatsigEnvironment

An object you can use to set environment variables that apply to all of your users in the same session.

logLevel LogLevel

How much information is allowed to be printed to the console.

default: LogLevel.Warn

loggingBufferMaxSize number

The maximum number of events to batch before flushing logs to Statsig.

default: 50

loggingIntervalMs number

How often (in milliseconds) to flush logs to Statsig.

default: 10,000 ms (10 seconds)

overrideAdapter OverrideAdapter

An implementor of OverrideAdapter, used to alter evaluations before its returned to the caller of a check api (checkGate/getExperiment etc).

includeCurrentPageUrlWithEvents boolean

(Web only) Should the 'current page' url be included with logged events.

default: true

disableStatsigEncoding boolean

Whether or not Statsig should use raw JSON for network requests where possible.

default: false

disableCompression boolean

Whether or not Statsig should compress JSON bodies for network requests where possible.

default: false

dataAdapter EvaluationsDataAdapter

An implementor of EvaluationsDataAdapter, used to customize the initialization/update flow.

default: StatsigEvaluationsDataAdapter

customUserCacheKeyFunc CustomCacheKeyGenerator

Overrides the default cache key generation. Given the SDKKey and current StatsigUser, return a key to be used for storing values related to that user.

default: Cache key is a hash of the sdkKey + user.userID + user.customIDs.

Manual Exposures

warning

Manually logging exposures can be tricky and may lead to an imbalance in exposure events. For example, only triggering exposures for users in the Test group of an experiment will imbalance the experiment, making it useless.

Added in version , you can now query your gates/experiments without triggering an exposure as well as manually logging your exposures.

To check a gate without an exposure being logged, call the following.

const result = myStatsigClient.checkGate('a_gate_name', { disableExposureLog: true });

Later, if you would like to expose this gate, you can call the following.

myStatsigClient.checkGate('a_gate_name');

Shutting Statsig Down

In order to save users' data and battery usage, as well as prevent logged events from being dropped, we keep event logs in client cache and flush periodically. Because of this, some events may not have been sent when your app shuts down.

To make sure all logged events are properly flushed or saved locally, you should tell Statsig to shutdown when your app is closing:

...

StableID

StableID is a concept in Statsig that allows us to have a consistent identifier for a single device. This allows Statsig to run experiments on logged out users (users without a UserID) as well as target gates on the device level rather than the user level.

How it works

When you initialize the SDK for the first time, it checks if a StableID is already present in local storage. If it's missing, the SDK generates a new StableID and saves it in local storage. On subsequent initializations, the SDK retrieves and reuses the previously stored StableID.

The StableID is stored in local storage under the key statsig.stable_id.<SDK_KEY_HASH>. Each SDK key has its own StableID, meaning that if you're using multiple SDK keys, each one will have a separate StableID.

It's important to note that local storage is not shared across different domains or subdomains. This means that if you are working across multiple domains or subdomains, the StableID will be isolated to each domain's local storage.

If you need a StableID to persist across domains, you'll need to implement your own mechanism to store and retrieve the StableID, such as using a cookie. By setting the StableID in a cookie with a shared domain scope, you can ensure that the same ID is accessible across multiple domains.

The Statsig SDK does not use any cookies itself.

Accessing the StableID

You can access StableID that the Statsig client is using by calling getContext() and then checking the stableID field.

...

Overriding StableID

If your app already has something similar to a StableID and you would prefer to use that instead, you can override the default behavior by simply passing your value in as part of the StatsigUser object.

...
note

Once overridden, the new StableID will be persisted to local storage so it will be used for future sessions.

Keeping StableID Consistent

If you have a backend service running a Statsig SDK as well as using the Statsig client in your frontend, you may need to have the same StableID accessible in both environments.

It is highly dependent on your setup, but usually you can send the StableID up with any requests from your frontend. If the backend ever receives a request without a StableID, it can generate one and have the client store it (usually as a cookie) for future requests.

Because Server SDKs are designed to handle multiple Users, they do not generate their own StableIDs.

Here is an example showing how you might use a cookie to share the StableID across client and server and use it bootstrap the client SDK.

...

Testing

When writing tests, you can use jest to mock Statsig packages.

Consider the following example function. We take in a string as input and based on some Statsig configurations, we edit the string and return the result.

...

In our jest test, we can mock the StatsigClient to return the values we want.

...

Session Replay

By including the @statsig/session-replay package in your project, you can automatically capture and log user sessions as videos. This feature is useful for debugging and understanding user behavior. Read more about Session Replay.

...

Web Analytics / Auto Capture

By including the @statsig/web-analytics package in your project, you can automatically capture common web events like clicks and page views. Read more about Web Analytics.

...

Advanced

Async timeouts

For the Statsig client calls that hit the network (initializeAsync and updateUserAsync), it is possible to specify a maximum amount of time (in milliseconds) that the method is allowed to take before the promise is resolved.

...

If this timeout is hit, the promise will resolve and cached values will be used (If any are available). The network request will continue and upon a response, will write values to cache so the will be available for subsequent calls.

If you'd like to avoid a network request entirely, see initialization strategies for more options.

Data Adapter

The EvaluationsDataAdapter type outlines how the StatsigClient should fetch and cache data during initialize and update operations. By default, the StatsigClient uses StatsigEvaluationsDataAdapter, a Statsig provided implementor of the EvaluationsDataAdapter type. StatsigEvaluationsDataAdapter provides ways to fetch data synchronously from Local Storage and asynchronously from Statsig's servers. See Using EvaluationsDataAdapter to learn more and see example usage.

Client Event Emitter

It is possible to subscribe to StatsigClientEvents (Not to be confused with StatsigEvent). These events occur at various stages while using the Statsig client. You can subscribe to specific events by specifying the StatsigClientEvent name, or, all events by using the wildcard token '*'.

...

The full list of events and descriptions can be found here.

Multiple Client Instances

In some cases, it might be useful to have multiple instances of the Statsig client running.

🔥IMPORTANT: You must use two different SDK keys for this to work. Various functionality of the Statsig client is keyed on the SDK key being used. Using the same key will lead to collisions.

...

Partial User Matching

In some advanced flows, you may have data that you want on your user objects, but do not want to make a network request to re-evaluate the user.

To achieve this, you can call updateUserSync with a altered user object and use StatsigOptions.customUserCacheKeyFunc to only match on the original user properties.


const originalUser = {
customIDs: {
analyticsID: "some-analytics-id-here"
},
}

const myCustomCacheKey = (sdkKey: string, user: StatsigUser) => {
const analyticsID = user.customIDs?.analyticsID;
return `sdkKey:${sdkKey}:analyticsID:${analyticsID}`; // <- Only match on analyticsID
}

const myStatsigClient = new StatsigClient("client-key-here", originalUser, {
customUserCacheKeyFunc: myCustomCacheKey // <- Pass in the custom cache key function
});
await myStatsigClient.initializeAsync();

someAsyncFunctionThatTakesAWhile().then((newData) => {
const alteredUser = {
...originalUser,
userID: newData.userID,
email: newData.email,
};

myStatsigClient.updateUserSync(alteredUser);
});
warning

Because we are only using analyticsID in the cache key, we will get cache hits for any user object with this ID. Potentially leading to incorrect evaluations.

For example, if we had a gate is_internal_user that was evaluating to true for all users with a specific UserID, but we were using a partial cache key, we would get a cache hit for all users with the same analyticsID.

await myStatsigClient.updateUserAsync({
userID: "internal_user_123",
customIDs: {
analyticsID: "MY_ANALYTICS_ID"
},
});

myStatsigClient.checkGate("is_internal_user"); // <- returns true
// ...

// Somewhere else in the code


myStatsigClient.updateUserSync({
userID: "prod_user_123",
customIDs: {
analyticsID: "MY_ANALYTICS_ID" // <- Same myAnalyticsID as internal_user_123
},
});

myStatsigClient.checkGate("is_internal_user"); // <- Still returns true because we got a cache hit from the internal user.

If you want to ensure you are always working with the latest values for the current user, you should await the call to updateUserAsync.

Debugging

If you run into issues when implementing Statsig, or are receiving values you do not understand, you can try some of the following to get yourself unstuck.

Enable Debug Logging

By setting the log level to the most verbose (Debug), the SDK will print more information around the internal operations it is performing.

import { LogLevel, StatsigClient, StatsigOptions } from '@statsig/js-client';

const client = new StatsigClient(YOUR_CLIENT_KEY, { userID: 'a-user'}, {
logLevel: LogLevel.Debug, // <- Print Everything
});

Access __STATSIG__ Global

If you see issues appearing, but only for certain environments, it can be useful to open the web browsers debugger and view the Statsig client instance.

  • Open the web browsers console. In Chrome, this is under View > Developer > JavaScript Console.
  • In the console, enter __STATSIG__. This will print the global Statsig state object, containing all internal state of the SDK.
  • eg __STATSIG__.instance()._logger._queue is the queued events array

STATSIG-global

View Network Logs

To see all traffic that is being sent via the Statsig SDK, you can filter your browsers network logs to those containing your client SDK key.

In Chrome, going to the Network tab of the developer tools and filtering to 'client-' is enough to show all the requests:

network-logs

Check Evaluation Reason

When you get a value that you don't fully understand, you can check the details field to see how that value was received.

For example, if you have a feature gate, you can check the .details.reason field:

const gate = myStatsigClient.getFeatureGate('a_gate');
console.log(gate.details.reason); // <- Prints the reason for this evaluation

Reasons can be any of the following:

  • Network | NetworkNotModified - The latest value from a network requests
  • Cache - Values loaded from local storage
  • NoValues - No cache and no successful network request
  • Bootstrap - Values found and loaded after a dataAdapter.setData call
  • Prefetch - Values found and loaded after a dataAdapter.prefetchData call

FeatureGate, DynamicConfig, Experiment and Layer types all have this details object. (Learn more at Debugging > Reasons)

FAQ

How do I run experiments for logged out users?

See the guide on device level experiments

Does the SDK use the browser local storage or cookies? If so, for what purposes?

The SDK does not use any cookies.

It does use the local storage for feature targeting and experimentation purposes only. Values for feature gates, dynamic configs and experiments are cached in the local storage, which are used as a backup in the event that your website/app cannot reach the Statsig server to fetch the latest values. If any events were logged but could not be sent to Statsig server due to issues like network failure, we also save them in the local storage to be sent again when network restores.

Can I reference the SDK instance globally?

Yes, StatsigClient instances can be accessed via the instance() function.

For example, if you initialize using a script tag in the head of your webpage, you can use the following to access the StatsigClient and log a custom event.

window.Statsig.instance().logEvent("test_event");

or, if you are importing the package:

import { StatsigClient } from '@statsig/js-client';

StatsigClient.instance().logEvent("test_event");

Note: in multi-instance setups, you can specify the SDK key to get a specific instance. eg Statsig.instance('client-YOUR_CLIENT_KEY')

In certain use cases, it is necessary to suspend cache and network usage until the user grants specific permissions. You can now start the SDK without storage or logging and enable this only after the user grants permission.

...