This guide provides detailed information about our React SDK. This library is built on top of our regular JavaScript SDK to ease the integration in React applications by providing a set of components with their equivalent HOCs, as well as helper functions based on the React Hooks API, so you can interact with the underneath SDK and work towards any use cases. All of our SDKs are open source. Go to our React SDK GitHub repository to see the source code.
Language support
This SDK requires React 16.3.0 or above, since it uses React Context API.
Some features, such as useSplitClient
and useSplitTreatments
, use React Hooks API that requires React 16.8.0 or later. Also remember to follow the rules of React hooks when using these. When it comes to browser support, our CDN bundle supports IE11+ and all the other major browsers.
Initialization
Set up Split in your code base in two steps.
1. Import the SDK into your project
You can import the SDK into your project using one of the following three methods:
npm install --save @splitsoftware/splitio-react
yarn add @splitsoftware/splitio-react
<!-- Don't forget to include React script tags before Split SDK. More details at https://reactjs.org/docs/add-react-to-a-website.html#step-2-add-the-script-tags -->
<script src="//cdn.split.io/sdk/splitio-react-1.12.1.min.js"></script>
2. Instantiate the SDK and create a new Split client
The code examples below show how to instantiate the SDK. The code creates a Split factory, which begins downloading your rollout plan so you can evaluate feature flags, and creates a client.
// CDN will expose the library globally with the "splitio" variable. Use the options that work best with your code.
const { SplitFactoryProvider } = window.splitio;
// Create the config for the SDK factory.
const sdkConfig = {
core: {
authorizationKey: 'YOUR_CLIENT_SIDE_SDK_KEY',
// key represents your internal user id, or the account id that
// the user belongs to.
// This could also be a cookie you generate for anonymous users.
key: 'key'
}
};
// Using the SplitFactoryProvider component, MyApp component and all its descendants have access to the SDK functionality.
const App = () => (
<SplitFactoryProvider config={sdkConfig} >
<MyApp />
</SplitFactoryProvider>
);
// The npm package exposes the different components and functions of the library as named exports.
const { SplitFactoryProvider } = require('@splitsoftware/splitio-react');
// Create the config for the SDK factory.
const sdkConfig = {
core: {
authorizationKey: 'YOUR_CLIENT_SIDE_SDK_KEY',
// key represents your internal user id, or the account id that
// the user belongs to.
// This could also be a cookie you generate for anonymous users.
key: 'key'
}
};
// Using the SplitFactoryProvider component, MyApp component and all its descendants have access to the SDK functionality.
const App = () => (
<SplitFactoryProvider config={sdkConfig} >
<MyApp />
</SplitFactoryProvider>
);
// The npm package exposes the different components and functions of the library as named exports.
import { SplitFactoryProvider } from '@splitsoftware/splitio-react';
// Create the config for the SDK factory.
const sdkConfig: SplitIO.IBrowserSettings = {
core: {
authorizationKey: 'YOUR_CLIENT_SIDE_SDK_KEY',
// key represents your internal user id, or the account id that
// the user belongs to.
// This could also be a cookie you generate for anonymous users.
key: 'key'
}
};
// Using the SplitFactoryProvider component, MyApp component and all its descendants have access to the SDK functionality.
const App: React.ComponentType = () => (
<SplitFactoryProvider config={sdkConfig} >
<MyApp />
</SplitFactoryProvider>
);
Deprecated components
The SplitFactory
and withSplitFactory
components have been deprecated, and will be removed in a future major release.
Although these components are still working, we recommend migrating to the new SplitFactoryProvider
component, available since version 1.11.0 of the SDK, because the SplitFactoryProvider
component has improved handling of the React component lifecycle.
Refer to this migration guide for more details.
For more flexibility as we wanted to support most use cases, the SplitFactoryProvider
component can receive the factory already instantiated for our React SDK to use it. If you're using the JavaScript SDK already, it is recommended that you follow the Singleton Pattern and keep only one instance of the factory at all times, which you can provide to the React SDK following the approach shown below.
You could access the JavasScript SDK factory function through the SplitSdk
named export of the React SDK too, which points to the original JavaScript SDK function so you don't need to import two different Split packages.
Note that you shouldn't mix the two options. Either provide a config or a factory instance. If both are provided, config will be ignored and you'll get a warning log.
When using the config
prop, the SplitFactoryProvider
component instantiates and shutdowns the SDK factory automatically when the component is mounted and unmounted respectively.
Therefore, you should use it for simpler setups where you know that the component state in the render tree will not reset, i.e., it will not be unmounted and mounted again. For instance, when you have a single SplitFactoryProvider
component in the root or near the root of your app. Otherwise, a new factory instance will be created if you passed a new config
object, or you might have a destroyed factory instance if you pass a reference to the same config
object and the component is mounted again.
When using the factory
prop, the SplitFactoryProvider
component doesn't shut down the SDK automatically. You should handle the shutdown yourself.
You can use it for more complex setups. For example, when you need to use the SplitFactoryProvider
component in different parts of your app, or when the SplitFactoryProvider
is nested in a component that might be unmounted and mounted again, for example in a tabbed view, or a micro-frontend architecture. In these cases, you can create the factory instance as a global variable, and pass it down to the SplitFactoryProvider
components.
// The npm package exposes the different components and functions of the library as named exports.
// If you were using the bundle via CDN, it'll be at `window.splitio.SplitSdk`
import { SplitSdk, SplitFactoryProvider } from '@splitsoftware/splitio-react';
// Create the Split factory object with your custom settings, using the re-exported function.
const factory: SplitIO.ISDK = SplitSdk({
core: {
authorizationKey: 'YOUR_CLIENT_SIDE_SDK_KEY',
key: 'key'
},
...
});
// Using the SplitFactoryProvider component, MyApp component and all it's descendants will have access to the SDK functionality using the provided factory.
const App: React.ComponentType = () => (
<SplitFactoryProvider factory={factory} >
<MyApp />
</SplitFactoryProvider>
);
Notice for TypeScript
With the SDK package on NPM, you get the SplitIO namespace, which contains useful types and interfaces for you to use.
Feel free to dive into the declaration files if IntelliSense is not enough!
We recommend instantiating the Split factory once as a singleton and reusing it throughout your application.
Configure the SDK with the SDK key for the Split environment that you would like to access. The SDK key is available in the Split UI, on your Admin settings page, API keys section. Select a client-side SDK API key. This is a special type of API token with limited privileges for use in browsers or mobile clients. See API keys to learn more.
Using the SDK
Deprecated hooks
If you're using the useClient
, useTreatments
or useManager
hooks, be aware that they have been deprecated and will be removed in a future major release.
While the deprecated hooks are still working, we recommend migrating to the new useSplitClient
, useSplitTreatments
and useSplitManager
hooks, available since version 1.10.0 of the SDK, because the new hooks provide a more flexible API.
Refer to this migration guide for more details.
Get treatments with configurations
When the SDK is instantiated, it kicks off background tasks to update an in-memory cache with small amounts of data fetched from Split servers. This process can take up to a few hundred milliseconds depending on the size of data. If the SDK is asked to evaluate which treatment to show to a customer for a specific feature flag while its in this intermediate state, it may not have the data necessary to run the evaluation. In this case, the SDK does not fail, rather, it returns the control treatment.
To make sure the SDK is properly loaded before asking it for a treatment, block until the SDK is ready, as shown below. We provide the isReady
boolean prop based on the client that will be used by the component. Internally we listen for the SDK_READY
event triggered by given SDK client to set the value of isReady
.
After the isReady
prop is set to true, you can use the SDK. SplitTreatments
component returns the proper treatments based on the names
prop value passed to it and the key
value you passed in the config when instantiating the SDK. Then use the treatments
prop to access the treatment values as well as the corresponding dynamic configurations that you defined in the Split user interface. Remember to handle the client returning control as a safeguard.
Similarly to the vanilla JS SDK, React SDK supports the ability to evaluate flags based on cached content when using LOCALSTORAGE as storage type. In this case, the isReadyFromCache
prop will change to true almost instantly since access to the cache is synchronous, allowing you to consume flags earlier on components that are critical to your UI. Keep in mind that the data might be stale until isReady
prop is true. Read more below.
import { SplitTreatments } from '@splitsoftware/splitio-react';
import MyComponentV1 from './MyComponentV1';
import MyComponentV2 from './MyComponentV2';
const featureName = 'FEATURE_FLAG_NAME';
/**
* This is one way to write a toggler component, which might be convenient for code cleanup afterwards
* as you remove both toggle and legacy component, then wire the version you'll keep.
* But it's not the only way to use the treatments. Always follow the pattern that works best for you!
**/
export default class MyComponentToggle extends React.Component {
renderContent(treatmentWithConfig) {
const { treatment, config } = treatmentWithConfig;
if (treatment === 'on') return (<MyComponentV2 config={config} {...this.props} />);
return (<MyComponentV1 config={config} {...this.props} />);
}
render() {
return (
<SplitTreatments names={[featureName]} >
{({ treatments, isReady }) => { // Passes down a TreatmentsWithConfig object and SplitContext properties like the boolean `isReady` flag.
return isReady ?
this.renderContent(treatments[featureName]) : // Use the treatments and configs.
<Spinner />; // Render a spinner if the SDK is not ready yet. You can do what you want with this boolean.
}}
</SplitTreatments>
);
}
}
import { useSplitTreatments } from '@splitsoftware/splitio-react';
import MyComponentV1 from './MyComponentV1';
import MyComponentV2 from './MyComponentV2';
const featureName = 'FEATURE_FLAG_NAME';
function renderContent(treatmentWithConfig, props) {
const { treatment, config } = treatmentWithConfig;
if (treatment === 'on') return (<MyComponentV2 config={config} {...props} />);
return (<MyComponentV1 config={config} {...props} />);
}
/**
* This is another way to write a toggler with a function component that uses hooks.
**/
function MyComponentToggle (props) {
// evaluate feature flags with the `useSplitTreatments` hook
const { treatments, isReady } = useSplitTreatments({ names: [featureName] });
return isReady ?
renderContent(treatments[featureName], props) : // Use the treatments and configs.
<Spinner />; // Render a spinner if the SDK is not ready yet.
}
export default MyComponentToggle;
The treatments property/value returned by this library has the following shape:
type TreatmentWithConfig = {
treatment: string,
config: string | null
};
type TreatmentsWithConfig = {
[featureName: string]: TreatmentWithConfig
};
As you can see from the object structure, the config will be a stringified version of the configuration JSON defined in the Split user interface. If there is no configuration defined for a treatment, the SDK returns null
for the config parameter.
In some instances, you may want to evaluate treatments for multiple feature flags at once using flag sets. In that case, you can use the SplitTreatments
component or the useSplitTreatments
hook with the flagSets
property rather than the names
property. Like names
, the flagSets
property is an array of string, each one corresponding to a different flag set name.
import { SplitTreatments } from '@splitsoftware/splitio-react';
import MyComponentV1 from './MyComponentV1';
import MyComponentV2 from './MyComponentV2';
const flagSets = ['frontend', 'client_side'];
const featureName = 'FEATURE_FLAG_NAME';
export default class MyComponentToggle extends React.Component {
renderContent(treatmentWithConfig) {
const { treatment, config } = treatmentWithConfig;
if (treatment === 'on') return (<MyComponentV2 config={config} {...this.props} />);
return (<MyComponentV1 config={config} {...this.props} />);
}
render() {
return (
<SplitTreatments flagSets={flagSets} >
{({ treatments, isReady }) => {
return isReady ?
// If your flag sets are not properly configured, `treatments` might be an empty object
// or it might not contain the feature flags you expect.
this.renderContent(treatments[featureName] || { treatment: 'control' }) :
<Spinner />;
}}
</SplitTreatments>
);
}
}
import { useSplitTreatments } from '@splitsoftware/splitio-react';
import MyComponentV1 from './MyComponentV1';
import MyComponentV2 from './MyComponentV2';
const flagSets = ['frontend', 'client_side'];
const featureName = 'FEATURE_FLAG_NAME';
function renderContent(treatmentWithConfig, props) {
const { treatment, config } = treatmentWithConfig;
if (treatment === 'on') return (<MyComponentV2 config={config} {...props} />);
return (<MyComponentV1 config={config} {...props} />);
}
function MyComponentToggle (props) {
const { treatments, isReady } = useSplitTreatments({ flagSets: flagSets });
return isReady ?
// If your flag sets are not properly configured, `treatments` might be an empty object
// or it might not contain the feature flags you expect.
renderContent(treatments[featureName] || { treatment: 'control' }, props) :
<Spinner />;
}
export default MyComponentToggle;
Attribute syntax
To target based on custom attributes, the SDK needs to be passed an attribute map at runtime. In the example below, we are rolling out a feature flag to users. The provided attributes plan_type
, registered_date
, permissions
, paying_customer
, and deal_size
are passed to the underlying getTreatmentsWithConfig
or getTreatmentsWithConfigByFlagSets
call, whether you are evaluating using the names
or flagSets
property respectively. These attributes are compared and evaluated against the attributes used in the rollout plan as defined in the Split user interface to decide whether to show the on
or off
treatment to this account. The SDK supports five types of attributes: strings, numbers, dates, booleans, and sets. The proper data type and syntax for each are:
- Strings: Use type String.
- Numbers: Use type Number.
-
Dates: Use type Date and express the value in
milliseconds since epoch
.
Note: Milliseconds since epoch is expressed in UTC. If your date or date-time combination is in a different timezone, first convert it to UTC, then transform it to milliseconds since epoch. - Booleans: Use type Boolean.
- Sets: Use type Array.
const attributes = {
// date attributes are handled as `millis since epoch`
registered_date: new Date('YYYY-MM-DDTHH:mm:ss.sssZ').getTime(),
// this string will be compared against a list called `plan_type`
plan_type: 'growth',
// this number will be compared agains a const value called `deal_size`
deal_size: 10000,
// this boolean will be compared against a const value called `paying_customer`
paying_customer: true,
// this array will be compared against a set called `permissions`
permissions: ["read", "write"]
};
/** If you're using the SplitTreatments component, you can use attributes by setting the "attributes" prop. **/
const ComponentWithTreatments = () =>
(<SplitTreatments names={[featureName]} attributes={attributes} >
{({ treatments, isReady }) => {
const treatment = treatments[featureName].treatment;
if (treatment === 'on') {
// do something
} else {
// do something else
}
}}
</SplitTreatments>)
/** If you're using the SplitTreatments HOC, you can use attributes by passing those as the second parameter. **/
const ComponentWithTreatments = withSplitTreatments([featureName], attributes)(
({ treatments, isReady }) => {
const treatment = treatments[featureName].treatment;
if (treatment === 'on') {
// do something
} else {
// do something else
}
});
const attributes: SplitIO.Attributes = {
// date attributes are handled as `millis since epoch`
registered_date: new Date('YYYY-MM-DDTHH:mm:ss.sssZ').getTime(),
// this string will be compared against a list called `plan_type`
plan_type: 'growth',
// this number will be compared agains a const value called `deal_size`
deal_size: 10000,
// this array will be compared against a set called `permissions`
permissions: ['read', 'write']
};
/** If you're using the SplitTreatments component, you can use attributes by setting the "attributes" prop. **/
const ComponentWithTreatments = () =>
(<SplitTreatments names={[featureName]} attributes={attributes} >
{({ treatments, isReady }) => {
const treatment: SplitIO.TreatmentWithConfig = treatments[featureName].treatment;
if (treatment === 'on') {
// do something
} else {
// do something else
}
}}
</SplitTreatments>)
/** If you're using the SplitTreatments HOC, you can use attributes by passing those as the second parameter. **/
const ComponentWithTreatments = withSplitTreatments([featureName], attributes)(
({ treatments, isReady }) => {
const treatment: SplitIO.TreatmentWithConfig = treatments[featureName].treatment;
if (treatment === 'on') {
// do something
} else {
// do something else
}
});
Binding attributes to the client
Attributes can optionally be bound to the SplitFactoryProvider
or SplitClient
components via props. These attributes are stored in memory and used in every evaluation to avoid the need to keep the attribute set accessible through the whole app. When an evaluation is called, for example, with the SplitTreatments
component or the useSplitTreatments
hook, the attributes provided (if any) at evaluation time are combined with the ones that are already loaded into the SDK memory, with the ones provided at evaluation time taking precedence. This enables those attributes to be overridden or hidden for specific evaluations.
An attribute is considered valid if it follows one of the types listed below:
- String
- Number
- Boolean
- Array
The SDK validates these before storing them and if there are invalid or missing values, possibly indicating an issue, the storing is not performed.
To use these methods, refer to the example below:
import { SplitFactoryProvider, SplitClient, SplitTreatments } from '@splitsoftware/splitio-react';
// Assuming this is how the user setup the factory:
const sdkConfig = {
core: {
authorizationKey: 'YOUR_CLIENT_SIDE_API_KEY',
key: 'nicolas@user'
}
};
const factoryAttributes = {
permissions: ["read", "write"]
};
const clientAttributes = {
permissions: "read"
};
const treatmentAttributes = {
deal_size: 10000
};
const App = () => (
<SplitFactoryProvider config={sdkConfig} attributes={factoryAttributes}>
<MyComponent accountId='nicolas@account' />
</SplitFactoryProvider>
);
class MyComponent extends React.Component {
render() {
return (
<div>
{
/*
* Using SplitTreatments as a descendant of the SplitFactoryProvider with no SplitClient wrapping it, uses the main client
* bound to the key passed in the config for evaluations and the attributes received on SplitFactoryProvider attributes prop.
*/
}
<SplitTreatments names={['USER_FEATURE_FLAG_NAME']} attributes={treatmentAttributes}>
{({ isReady, treatments }) => {
// Do something with the treatments for the 'nicolas@user' key.
// This evaluation combines SplitFactoryProvider component attributes with the ones on the SplitTreatments component.
// Then it evaluates with attributes = { permissions: ["read", "write"], deal_size: 10000 }
}}
</SplitTreatments>
{
/*
* To use another client, use the SplitClient component. Keep in mind that
* this client might not be ready yet if it's just being created and still downloading segments
* for the new key. Use the `isReady` property to check that.
*/
}
<SplitClient splitKey={this.props.accountId} attributes={clientAttributes} >
<SplitTreatments names={['ACCOUNT_FEATURE_FLAG_NAME', 'ACCOUNT_FEATURE_FLAG_NAME_2']} attributes={treatmentAttributes} >
{({ isReady, treatments }) => {
// Do something with the treatments for the accountId key.
// This evaluation is for another client, so factory attributes stored in main client (factory) will not be taking part.
// This evaluation combines `treatmentAttributes` with `clientAttributes`.
// The evaluation attributes are { permissions: "read", deal_size: 10000 }
}}
</SplitTreatments>
</SplitClient>
</div>
);
}
}
import { SplitFactoryProvider, useSplitClient, useSplitTreatments } from '@splitsoftware/splitio-react';
// Assuming this is how the user setup the factory:
const sdkConfig = {
core: {
authorizationKey: 'YOUR_CLIENT_SIDE_API_KEY',
key: 'nicolas@user'
}
};
const factoryAttributes = {
permissions: ["read", "write"]
};
const clientAttributes = {
permissions: "read"
};
const treatmentAttributes = {
deal_size: 10000
};
const App = () => (
<SplitFactoryProvider config={sdkConfig} attributes={factoryAttributes}>
<MyComponent accountId='nicolas@account' />
</SplitFactoryProvider>
);
function MyComponent(props) {
/*
* Using useSplitTreatments as a descendant of the SplitFactoryProvider with no SplitClient wrapping it, uses the main client
* bound to the key passed in the config for evaluations and the attributes received on SplitFactoryProvider attributes prop.
*/
const { isReady, treatments } = useSplitTreatments({ names: ['USER_FEATURE_FLAG_NAME'], attributes: treatmentAttributes });
// Do something with the treatments for the 'nicolas@user' key.
// This evaluation combines SplitFactoryProvider component attributes with the ones provided to the useSplitTreatments hook.
// Then it evaluates with attributes = { permissions: ["read", "write"], deal_size: 10000 }
// You can retrieve clients for as many keys as you need, although it's not recommended to create more than you absolutely need to.
// Keep in mind that if it is the first time you retrieve a client, it might not be ready yet.
const { client: accountClient, isReady } = useSplitClient({ splitKey: this.props.accountId, attributes: clientAttributes });
if (isReady) {
// `accountClient` is ready to evaluate treatments as usual. To see more information about the client API, refer to the docs for JavaScript SDK.
// In this evaluation, `treatmentAttributes`` are combined with `clientAttributes`
const accountTreatments = accountClient.getTreatments(['ACCOUNT_FEATURE_FLAG_NAME', 'ACCOUNT_FEATURE_FLAG_NAME_2'], treatmentAttributes });
// Do something with the treatments for the accountId key.
// This evaluation is for another client, so factory attributes stored in main client (factory) will not be taking part.
// This evaluation combines `treatmentAttributes` with `clientAttributes`.
// The evaluation attributes are { permissions: "read", deal_size: 10000 }
return (<div>{...}</div>);
} else {
// `accountClient` is not ready
return (<div>{...}</div>);
}
}
If the SplitFactoryProvider
component is created with attributes and it has a SplitClient
component where the evaluation is called, the attributes in SplitFactoryProvider
will not take part in the evaluation. If you want those attributes to be combined and evaluated, make the evaluation in the SplitFactoryProvider
component itself.
Not setting any attributes on the SplitFactoryProvider
or SplitClient
component works like a clearAttributes
method for that component and removes every attribute stored on the respective client.
Shutdown
If the SplitFactoryProvider
component is created with a config
prop, it instantiates and shutdowns the SDK factory automatically when the component is created and unmounted respectively. Otherwise, if the component is created passing an SDK factory via the factory
prop, the shutdown must be handled by the user.
Track
Use the client.track
method (here is how to access the client) or useTrack
hook function to record any actions your customers perform. Each action is known as an event
and corresponds to an event type
. Tracking events through one of our SDKs or via the API is the first step to getting experimentation data into Split and allows you to measure the impact of your features on your users' actions and metrics.
Learn more about using track events in feature flags.
In the examples below, you can see that tracking events can take up to four arguments. The proper data type and syntax for each are:
- TRAFFIC_TYPE: The traffic type of the key in the track call. The expected data type is String. You can only pass values that match the names of traffic types that you have defined in your instance of Split.
-
EVENT_TYPE: The event type that this event should correspond to. The expected data type is String. Full requirements on this argument are:
- Contains 63 characters or fewer.
- Starts with a letter or number.
- Contains only letters, numbers, hyphen, underscore, or period.
- This is the regular expression we use to validate the value:
[a-zA-Z0-9][-_\.a-zA-Z0-9]{0,62}
- VALUE: (Optional) The value to be used in creating the metric. This field can be sent in as null or 0 if you intend to purely use the count function when creating a metric. The expected data type is Integer or Float.
- PROPERTIES: (Optional) An object of key value pairs that can be used to filter your metrics. Learn more about event property capture. Split currently supports three types of properties: strings, numbers, and booleans.
The underlying track
method returns a boolean value of true
or false
to indicate whether or not the SDK was able to successfully queue the event to be sent back to Split's servers on the next event post. The SDK returns false
if the current queue size is equal to the config set by eventsQueueSize
config or if an incorrect input has been provided.
In case a bad input is provided, you can read more about our SDK's expected behavior.
Remember that for using the useTrack
hook, you must follow React Hook rules. Therefore, in contexts where they are not allowed, like inside class-based React components, you must track events accessing the SDK client directly, as shown in the examples below:
import { useTrack } from '@splitsoftware/splitio-react';
function MyComponent() {
// With the useTrack hook you can get access to the regular .track() method of the client.
const track = useTrack();
// If you need to track events for a different key or just want to make sure you're tracking for the right key.
const otherTrack = useTrack('key');
// Here's what the track function signature looks like:
// track(trafficType: string, eventType: string, value?: number, properties?: Properties): boolean;
// Track events into an effect to make sure they're not tracked on every render.
useEffect(() => {
// Now you can track your events by passing at least the traffic type and the event type.
let queued = track('user', 'logged_in');
// Example with both a value and properties
const properties = { package : "premium", admin : true, discount : 50 };
queued = track('user', 'page_load_time', 83.334, properties);
// If the event doesn't have a value but do have properties, skip the value by passing it null.
queued = track('user', 'logged_in', null, properties);
}, []);
// Alternatively, you can also bind the trafficType to the track method for convenience by either calling useTrack with a traffic type:
const track = useTrack('key', 'user');
// Or by passing the traffic type to the SDK config when initializing, so the default track function will be already bound.
// NOTE: Providing the traffic type in the config is only recommended if you won't track events for more than one traffic type for the given key and want to save passing the parameter.
const track = useTrack();
// Now you can call the tracker function as always without the need to provide a traffic type.
// Signature of the function would be:
// track(eventType: string, value?: number, properties?: Properties): boolean;
useEffect(() => {
// Same examples as before but now we don't need the traffic type.
queued = track('logged_in');
queued = track('page_load_time', 83.334, properties);
queued = track('logged_in', null, properties);
}, []);
import { useTrack } from '@splitsoftware/splitio-react';
function MyComponent() {
// With the useTrack hook you can get access to the regular .track() method of the client, with the following signature
let track: (trafficType: string, eventType: string, value?: number, properties?: SplitIO.Properties): boolean = useTrack();
// If you need to track events for a different key or just want to make sure you're tracking for the right key
track = useTrack('key');
// Track events into an effect to make sure they're not tracked on every render.
useEffect(() => {
// Now you can track your events by passing at least the traffic type and the event type.
let queued: boolean = track('user', 'logged_in');
// Example with both a value and properties
const properties: SplitIO.Properties = { package : "premium", admin : true, discount : 50 };
queued = track('user', 'page_load_time', 83.334, properties);
// If the event doesn't have a value but does have properties, skip the value by passing it null.
queued = track('user', 'logged_in', null, properties);
}, []);
// Alternatively, you can also bind the trafficType to the track method for convenience by either calling useTrack with a traffic type:
let otherTrack: (eventType: string, value?: number, properties?: SplitIO.Properties): boolean = useTrack('key', 'user');
// Or by passing the traffic type to the SDK config when initializing, so the default track function will be already bound.
// NOTE: Providing the traffic type in the config is only recommended if you won't track events for more than one traffic type for the given key and want to save passing the parameter.
otherTrack = useTrack();
useEffect(() => {
// Same examples as before but now we don't need the traffic type
queued = otherTrack('logged_in');
queued = otherTrack('page_load_time', 83.334, properties);
queued = otherTrack('logged_in', null, properties);
}, []);
import { SplitClient, SplitFactoryProvider, SplitTreatments } from '@splitsoftware/splitio-react';
// you can access the SDK client as a render prop passed down by the `SplitFactoryProvider` component
const App = () => (
<SplitFactoryProvider config={localstorageConfig} >
{({ isReady, factory, client }) => {
// `client` is the SDK client which key (or user id) is defined in the SDK config
let queued = client.track('user', 'logged_in');
// this is equivalent to:
queue = factory.client().track('user', 'logged_in');
...
}}
</SplitFactoryProvider>
);
// you can also access the SDK client passed down by the `SplitTreatments` and `SplitClient` components
const App = () => {
return (
<SplitFactoryProvider config={localstorageConfig} >
<SplitTreatments names={['some-split']}>
{({ isReady, factory, client }) => {
// `client` is the main client, i.e., `factory.client()`
const queued = client.track('user', 'logged_in');
...
}}
</SplitTreatments>
<SplitClient splitKey='key' >
{({ isReady, factory, client }) => {
// `client` here is the new client with 'key' user id
const queued = client.track('user', 'logged_in');
...
}}
</SplitClient>
<SplitClient splitKey='key2' trafficType='user'>
{({ isReady, factory, client }) => {
// `client` is another client with 'key2' as user id and bound traffic type
const queued = client.track('logged_in');
...
}}
</SplitClient>
</SplitFactoryProvider>
)
}
Configuration
The SDK has a number of knobs for configuring performance. Each knob is tuned to a reasonable default. However, you can override the value while providing the config to the SplitFactoryProvider
as shown in the Initialization section of this doc. To learn about the available configuration options, go to the JavaScript SDK Configuration section.
Localhost mode
For testing, a developer can put code behind feature flags on their development machine without the SDK requiring network connectivity. To do this, start the Split SDK in localhost mode (also called off-the-grid mode). In this mode, the SDK neither polls or updates from Split servers. Instead, it uses an in-memory data structure to determine what treatments to show to the logged in customer for each of the features.
When instantiating the SDK in localhost mode, your authorizationKey
is "localhost"
. Define the feature flags you want to use in the features
object map. All SplitTreatments
components and useSplitTreatments
calls for a feature flag return the treatment (and config, if defined) that you have defined in the map. You can then change the treatment as necessary for your testing. If you want to update a treatment or a config, or to add or remove feature flags from the mock cache, update the properties of the features
object you've provided. The SDK simulates polling for changes and updates from it. Do not assign a new object to the features
property because the SDK has a reference to the original object and will not detect the change.
Any feature that is not provided in the features
map returns the control treatment if the SDK is asked to evaluate them. Use the following additional configuration parameters when instantiating the SDK in localhost
mode:
Configuration | Description | Default value |
---|---|---|
scheduler.offlineRefreshRate | The refresh interval for the mocked features treatments. | 15 |
features | A fixed mapping of which treatment to show for our mocked features. | {} By default we have no mocked features. |
To use the SDK in localhost mode, replace the SDK Key with "localhost", as shown in the following test examples. Note that you can define the object between a feature flag name and treatment directly or use a map to define both a treatment and a dynamic configuration.
If you define just a string as the value for a feature flag name, the config returned by the SDK is null. If you use a map, we return the specified treatment and the specified config, which can also be null.
import React from "react";
// React testing library: https://www.npmjs.com/package/@testing-library/react
import { render, waitFor } from "@testing-library/react";
import { SplitFactoryProvider, SplitTreatments } from "@splitsoftware/splitio-react";
const config = {
core: {
authorizationKey: 'localhost',
key: 'user_id'
},
features: {
'reporting_v2': 'on', // example with just a string value for the treatment
'billing_updates': { treatment: 'visa', config: '{ "color": "blue" }' }, // example of a defined config
'show_status_bar': { treatment: 'off', config: null } // example of a null config
}
};
const MyApp = () => {
return (
<SplitFactoryProvider config={config} >
<SplitTreatments names={['reporting_v2']} >
{({ isReady, treatments }) => {
return <div>{`${isReady ? 'SDK ready.' : 'SDK not ready.'} Feature flag reporting_v2 is ${treatments['reporting_v2'].treatment}`}</div>;
}}
</SplitTreatments>
</SplitFactoryProvider>
);
};
describe('MyApp', () => {
test('renders the correct treatment', async () => {
const { getByText, findByText } = render(<MyApp/>);
// On the initial rendered output, the SDK is not ready. So treatment values are control.
expect(getByText('SDK not ready. Feature flag reporting_v2 is control')).toBeTruthy();
// In localhost mode, the SDK is ready and the component re-rendered with the mocked treatment on next event-loop tick.
// So we use `findByText` to wait for the component to update.
expect(await findByText('SDK ready. Feature flag reporting_v2 is on')).toBeTruthy();
// `waitFor` (https://testing-library.com/docs/dom-testing-library/api-async/#waitfor) can also be used:
expect(await waitFor(() => getByText('SDK ready. Feature flag reporting_v2 is on')));
});
});
import React from 'react';
// Enzyme testing utility: https://www.npmjs.com/package/enzyme
import { mount } from 'enzyme';
import { SplitFactoryProvider, SplitTreatments } from '@splitsoftware/splitio-react';
const config = {
core: {
authorizationKey: 'localhost',
key: 'user_id'
},
features: {
'reporting_v2': 'on', // example with just a string value for the treatment
'billing_updates': { treatment: 'visa', config: '{ "color": "blue" }' }, // example of a defined config
'show_status_bar': { treatment: 'off', config: null } // example of a null config
}
};
const MyApp = () => {
return (
<SplitFactoryProvider config={config} >
<SplitTreatments names={['reporting_v2']} >
{({ isReady, treatments }) => {
return <div>{`${isReady ? 'SDK ready.' : 'SDK not ready.'} Feature flag reporting_v2 is ${treatments['reporting_v2'].treatment}`}</div>;
}}
</SplitTreatments>
</SplitFactoryProvider>
);
};
describe('MyApp', () => {
test('renders the correct treatment', (done) => {
const wrapper = mount(<MyApp/>);
// On the initial rendered output, the SDK is not ready. So treatment values are control.
expect(wrapper.html().includes('SDK not ready. Feature flag reporting_v2 is control')).toBeTruthy();
// In localhost mode, the SDK is ready and the component re-rendered with the mocked treatment on next event-loop tick.
// So we call `wrapper.update()` in a timeout callback to re-render the component.
setTimeout(() => {
wrapper.update();
expect(wrapper.html().includes('SDK ready. Feature flag reporting_v2 is on')).toBeTruthy();
done();
}, 0);
});
});
import React from 'react';
// React Test Renderer: https://reactjs.org/docs/test-renderer.html
import { create } from 'react-test-renderer';
import { SplitFactoryProvider, SplitTreatments } from '@splitsoftware/splitio-react';
const config = {
core: {
authorizationKey: 'localhost',
key: 'user_id'
},
features: {
'reporting_v2': 'on', // example with just a string value for the treatment
'billing_updates': { treatment: 'visa', config: '{ "color": "blue" }' }, // example of a defined config
'show_status_bar': { treatment: 'off', config: null } // example of a null config
}
};
const MyApp = () => {
return (
<SplitFactoryProvider config={config} >
<SplitTreatments names={['reporting_v2']} >
{({ isReady, treatments }) => {
return <div>{`${isReady ? 'SDK ready.' : 'SDK not ready.'} Feature flag reporting_v2 is ${treatments['reporting_v2'].treatment}`}</div>;
}}
</SplitTreatments>
</SplitFactoryProvider>
);
};
describe('MyApp', () => {
test('renders the correct treatment', (done) => {
const root = create(<MyApp/>);
// On the initial rendered output, the SDK is not ready. So treatment values are control.
expect(root.toJSON().children[0]).toEqual('SDK not ready. Feature flag reporting_v2 is control');
// In localhost mode, the SDK is ready and the component re-rendered with the mocked treatment on next event-loop tick.
// So we call `root.update` in a timeout callback to re-render the component.
setTimeout(() => {
root.update(<MyApp/>);
expect(root.toJSON().children[0]).toEqual('SDK ready. Feature flag reporting_v2 is on');
done();
}, 0);
});
});
Manager
Use the Split manager to get a list of features available to the Split client. To access the Manager in your code base, use the useSplitManager
hook from the library inside the context of a SplitFactoryProvider
component.
import { useSplitManager } from '@splitsoftware/splitio-react';
const { manager, isReady } = useSplitManager();
// Make sure the SDK is ready to return Split's data from the manager object
if (isReady) {
// Manager is ready to be used.
const flagNames = manager.names();
}
import { useSplitManager } from '@splitsoftware/splitio-react';
const { manager, isReady } = useSplitManager();
// Make sure the SDK is ready to return Split's data from the manager object
if (isReady) {
// Manager is ready to be used.
const flagNames: SplitIO.SplitNames = manager.names();
}
To find all the details on the Manager available methods, see the JavaScript SDK Manager section.
Listener
Split SDKs send impression data back to Split servers periodically and as a result of evaluating feature flags. To additionally send this information to a location of your choice, define and attach an impression listener. For that purpose, the SDK's configurations have a parameter called impressionListener
where an implementation of ImpressionListener
could be added. This implementation must define the logImpression
method and it receives data in the following schema.
Name | Type | Description |
---|---|---|
impression | Object | |
attributes | Object | A map of attributes used on the evaluation (if any). |
sdkLanguageVersion | String | The version of the SDK. In this case the language is javascript plus the version of the underlying SDK. |
Note
There are two additional keys on this object, ip
and hostname
. They are not used on the browser.
Implement custom impression listener
The following is an example of how to implement a custom impression listener:
import { SplitFactoryProvider } from '@splitsoftware/splitio-react';
function logImpression(impressionData) {
// do something with the impression data.
}
// Create the config for the SDK factory.
const sdkConfig = {
core: {
authorizationKey: 'YOUR_CLIENT_SIDE_SDK_KEY',
key: 'key'
},
impressionListener: {
logImpression: logImpression
}
});
// Using the SplitFactoryProvider component.
const App = () => (
<SplitFactoryProvider config={sdkConfig} >
<MyApp />
</SplitFactoryProvider>
);
import { SplitFactoryProvider } from '@splitsoftware/splitio-react';
class MyImprListener implements SplitIO.IImpressionListener {
logImpression(impressionData: SplitIO.ImpressionData) {
// do something with impressionData
}
}
// Create the config for the SDK factory.
const sdkConfig = {
core: {
authorizationKey: 'YOUR_CLIENT_SIDE_SDK_KEY',
key: 'key'
},
impressionListener: {
logImpression: new MyImprListener()
}
});
// Using the SplitFactoryProvider component.
const App: React.ComponentType = () => (
<SplitFactoryProvider config={sdkConfig} >
<MyApp />
</SplitFactoryProvider>
);
An impression listener is called asynchronously from the corresponding evaluation, but is almost immediate.
Even though the SDK does not fail if there is an exception in the listener, do not block the call stack.
Logging
To enable SDK logging in the browser, see how the SDK Logging works.
This library own logger is not configurable yet, but will be very soon!
Advanced use cases
This section describes advanced use cases and features provided by the SDK.
Instantiate multiple SDK clients
Each JavaScript SDK client is tied to one specific customer id at a time which usually belongs to one traffic type (for example, user
, account
, organization
). This enhances performance and reduces data cached within the SDK.
Split supports the ability to release based on multiple traffic types. With traffic types, you can release to users
in one feature flag and accounts
in another. If you are unfamiliar with using multiple traffic types, you can learn more here.
If you need to roll out feature flags by different traffic types, instantiate multiple SDK clients, one for each traffic type. For example, you may want to roll out the feature user-poll
by users
and the feature account-permissioning
by accounts
. You can do this by generating multiple clients using the SplitClient
component or its equivalent withSplitClient
HOC, so that every descendant component is tied to the specified client object.
If you don't want to change the instance in your context but need to get a flag for a different ID, you can do it by interacting directly with the different client objects by using the useSplitClient
hook.
See some examples below:
import { SplitFactoryProvider, SplitClient } from '@splitsoftware/splitio-react';
// Assuming this is how we setup the factory:
const sdkConfig = {
core: {
authorizationKey: 'YOUR_CLIENT_SIDE_SDK_KEY',
key: 'nicolas@split'
}
};
const App = () => (
<SplitFactoryProvider config={sdkConfig} >
<MyComponentWithFlags accountId='nicolas@account' />
</SplitFactoryProvider>
);
class MyComponentWithFlags extends React.Component {
// See how you can use the component to evaluate for multiple clients in your templates.
render() {
return (
<div>
{
/*
* Using SplitTreatments as a descendant of the SplitFactoryProvider with no SplitClient wrapping it,
* uses the main client bound to the key passed in the config for evaluations.
* In this case the used key for evaluations is 'nicolas@split'.
*/
}
<SplitTreatments names={['USER_FEATURE_FLAG_NAME']} >
{({ isReady, treatments }) => {
// Do something with the treatments for the user traffic type.
}}
</SplitTreatments>
{
/*
* To use another client, you can use the SplitClient component. Keep in mind that
* this client might not be ready yet if it's just being created and still downloading segments
* for the new key, but you can use the isReady property to block until ready.
*/
}
<SplitClient splitKey={this.props.accountId} >
<SplitTreatments names={['ACCOUNT_FEATURE_FLAG_NAME', 'ACCOUNT_FEATURE_FLAG_NAME_2']} >
{({ isReady, treatments }) => {
// Do something with the treatments for the account traffic type.
}}
</SplitTreatments>
</SplitClient>
</div>
);
}
}
import { SplitFactoryProvider, useSplitClient } from '@splitsoftware/splitio-react';
// Assuming this is how we setup the factory:
const sdkConfig = {
core: {
authorizationKey: 'YOUR_CLIENT_SIDE_SDK_KEY',
key: 'nicolas@split'
}
};
const App = () => (
<SplitFactoryProvider config={sdkConfig} >
<MyComponentWithFlags accountId='nicolas@account' />
</SplitFactoryProvider>
);
// An example of how you can access a different SDK client to track events or evaluate flags in a function component with `useSplitClient` hook.
function MyComponentWithFlags(props) {
// Calling useSplitClient with no parameters returns the client on the context of this component. If MyComponentWithFlags
// is not descendant of a SplitClient component, it returns the main client associated with the key provided on initialization.
// In this case the client associated with 'nicolas@split'
const { client: inContextClient } = useSplitClient();
// If client was already instantiated, it is just a getter like in the regular JS SDK.
const { client: myUserClient } = useSplitClient({ splitKey: 'nicolas@split' });
console.log(inContextClient === myUserClient); // logs "true"
// You can retrieve clients for as many keys as you need, although not recommended to create more than you absolutely need to.
// Keep in mind that if it is the first time you retrieve a client, it might not be ready yet.
const { client: accountClient, isReady: isReadyAccountClient } = useSplitClient({ splitKey: this.props.accountId });
if (isReadyAccountClient) {
// `accountClient` is ready to evaluate treatments as usual. To see more information about the client API go to the docs for JavaScript SDK.
const accountTreatments = accountClient.getTreatments(['ACCOUNT_FEATURE_FLAG_NAME', 'ACCOUNT_FEATURE_FLAG_NAME_2']);
return (<div>{...}</div>);
} else {
// `accountClient` is not ready
return (<div>{...}</div>);
}
}
Number of SDK instances
While the SDK does not put any limitations on the number of instances that can be created, we strongly recommend keeping the number of SDKs down to one or two.
Subscribe to events
The underlying JavaScript SDK has four different events:
-
SDK_READY_FROM_CACHE
. This event fires once the SDK is ready to evaluate treatments using a version of your rollout plan cached in localStorage from a previous session (which might be stale). If there is data in localStorage, this event fires almost immediately, since access to localStorage is fast; otherwise, it doesn't fire. -
SDK_READY
. This event fires once the SDK is ready to evaluate treatments using the most up-to-date version of your rollout plan, downloaded from Split servers. -
SDK_READY_TIMED_OUT
. This event fires if there is no cached version of your rollout plan cached in localStorage, and the SDK could not download the data from Split servers within the time specified by thereadyTimeout
configuration parameter. This event does not indicate that the SDK initialization was interrupted. The SDK continues downloading the rollout plan and fires theSDK_READY
event when finished. This delayedSDK_READY
event may happen with slow connections or large rollout plans with many feature flags, segments, or dynamic configurations. -
SDK_UPDATE
. This event fires whenever your rollout plan is changed. Listen for this event to refresh your app whenever a feature flag or segment is changed in the Split user interface.
While you could potentially access the JavaScript SDK client instances used by the React library and track this yourself, this is not trivial. We accept four optional boolean props for both the SplitFactoryProvider
and SplitClient
components, as well as their HOC versions. If these props are set to true
, their context updates in the event of an SDK_READY
, SDK_READY_FROM_CACHE
, SDK_UPDATE
or SDK_READY_TIMED_OUT
being emitted, so their child components will re-render and you can take action if you desire to do so.
The useSplitClient
and useSplitTreatments
hooks also accept these properties as optional parameters. If they are set to true
, the hook will trigger a re-render of the component on the corresponding event.
- For
SDK_READY
, you can set the propupdateOnSdkReady
. Default istrue
. - For
SDK_READY_FROM_CACHE
, you can set the propupdateOnSdkReadyFromCache
. Default istrue
. - For
SDK_READY_TIMED_OUT
, you can set the propupdateOnSdkTimedout
. Default isfalse
. - For
SDK_UPDATE
, you can set the propupdateOnSdkUpdate
. Default isfalse
.
In particular, some properties at the context are updated and passed down as props to the child of SplitFactoryProvider
, SplitClient
, and SplitTreatments
components, if they are defined as functions instead of React elements. Also the useSplitClient
and useSplitTreatments
hooks return the same properties.
These properties consist of the following status properties:
-
isReady
: a boolean indicating if theSDK_READY
event was triggered. -
isReadyFromCache
: a boolean indicating if theSDK_READY_FROM_CACHE
event was triggered. -
hasTimedout
: a boolean indicating if theSDK_READY_TIMED_OUT
event was triggered. -
isTimedout
: a boolean indicating if theSDK_READY_TIMED_OUT
event was triggered and the SDK is not ready to be consumed. Formally,isTimedout
is equivalent tohasTimeout && !isReady
. -
lastUpdate
: timestamp of the last listened event.
Find an example below:
function MyApp() {
// Evaluates feature flags for the main client bound to the key passed in the factory config.
const { treatments, isReady, isReadyFromCache, hasTimedout, lastUpdate } = useSplitTreatments({ names: ['USER_FEATURE_FLAG_NAME'] });
// But we can evaluate at a per client basis, and choose when to trigger a re-render.
// For example, the accountId we only want to update on SDK_READY and SDK_READY_TIMED_OUT. Not on SDK_READY_FROM_CACHE or SDK_UPDATE.
const { treatments: accountTreatments, isReady: isReadyAccount, hasTimedout: hasTimedoutAccount } = useSplitTreatments({
names: ['ACCOUNT_FEATURE_FLAG_NAME', 'ACCOUNT_FEATURE_FLAG_NAME_2'],
splitKey: accountId,
updateOnSdkReadyFromCache: false, // true by default.
updateOnSdkTimedout: true // false by default.
// `updateOnSdkUpdate` is false by default and `updateOnSdkReady` is true by default.
});
return (
<div>
{
// Do something with the treatments for the main key
}
{
// Do something with the treatments for the account key
}
</div>
);
};
const App = () => (
/* Since MyApp is a child of the SplitFactoryProvider component, it re-renders on SDK_READY, SDK_READY_FROM_CACHE,
* and SDK_UPDATE events, but not on SDK_READY_TIMED_OUT (because updateOnSdkTimedout is false).
*/
<SplitFactoryProvider config={sdkConfig} updateOnSdkUpdate={true} updateOnSdkTimedout={false} >
<MyApp />
</SplitFactoryProvider>
);
function MyApp({ isReady, isReadyFromCache, isTimedout, hasTimedout, lastUpdate, factory, client }) {
return (
<div>
<SplitTreatments names={['USER_FEATURE_FLAG_NAME']} >
{
// Do something with the treatments for the user key defined in the config.
}
</SplitTreatments>
{ /* But we can override that setup at a per client basis, so the account one we only want to
* update on SDK_READY and SDK_READY_TIMED_OUT */ }
<SplitClient splitKey={accountId} updateOnSdkReadyFromCache={false} updateOnSdkUpdate={false} updateOnSdkTimedout={true} >
<SplitTreatments names={['ACCOUNT_FEATURE_FLAG_NAME', 'ACCOUNT_FEATURE_FLAG_NAME_2']} >
{({ isReady, isReadyFromCache, isTimedout, hasTimedout, lastUpdate, treatments }) => {
// Do something with the treatments for the `accountId` key.
}}
</SplitTreatments>
</SplitClient>
</div>
);
};
const App = () => (
/* Here MyApp function component is passed as a render prop component and not as a React element like <MyApp>.
* MyApp is called with the SplitContext object as param, which contains the SDK factory, client and status properties.
* Since this is a child of the SplitFactoryProvider component, it is called again on SDK_READY, SDK_READY_FROM_CACHE,
* and SDK_UPDATE events, but not on SDK_READY_TIMED_OUT (because updateOnSdkTimedout is false).
*/
<SplitFactoryProvider config={sdkConfig} updateOnSdkUpdate={true} updateOnSdkTimedout={false} >
{MyApp}
</SplitFactoryProvider>
);
You can also access the SplitContext
directly in your components for checking the readiness state of the client. Via the React useContext
function, you can access the value of the SplitContext
as shown below:
import { useContext } from 'react';
import { SplitContext } from "@splitsoftware/splitio-react";
const MyComponent = () => {
const { isReady, isTimedout } = useContext(SplitContext);
return isReady || isTimedout ?
<MyFeature /> :
<Loading />
}
import { useContext } from 'react';
import { SplitContext } from "@splitsoftware/splitio-react";
const MyComponent: React.ComponentType = () => {
const { isReady, isTimedout }: ISplitContextValues = useContext(SplitContext);
return isReady || isTimedout ?
<MyFeature /> :
<Loading />
}
The SplitContext
value object has the following structure:
interface SplitContextValue {
factory: SplitIO.ISDK,
client: SplitIO.IClient,
isReady: boolean,
isReadyFromCache: boolean,
hasTimeout: boolean,
isTimedout: boolean,
isDestroyed: boolean,
lastUpdate: number,
}
The SplitContext
exposes the internal factory and client instances of JavaScript SDK which is used underneath. While the React SDK should enable most use cases when using React, you might be in a situation where you must use the SDK functionality directly outside React component, this should give you enough flexibility. We discourage direct use of these instances unless you must.
User consent
The SDK factory allows you to disable the tracking of events and impressions until user consent is explicitly granted or declined. To learn how to configure this feature, refer to the JavaScript SDK User consent section.
Server-Side Rendering
The SDK follows the rules of the React component lifecycle by correctly handling the SDK factory creation and destroy side-effects and supporting running its components on the server side.
When running server side, the factory
prop of the SplitFactoryProvider
component should remain unassigned. This avoids an hydration mismatch between the server and the client, and also avoids having to create a factory instance on each server request.
The config
prop of the SplitFactoryProvider
can be used instead of the factory
prop. This way, the HTML rendered on the server side will match the first render on the client side, because both sides will conditionally render your components with uninstantiated SDK objects. At this point, status properties like isReady
are false
, the treatments retrieved using useSplitTreatments
are 'control'
, and factory
, and client
properties in the Split context are null
. Following this state, the SDK factory is created on the SplitFactoryProvider
effect. Thus, the SDK factory will be created on the client side and available on a subsequent render, but will not be created on the server side.
See the SplitFactoryProvider migration guide to learn more about SDK improvements for SSR.
Usage for SSR is shown in the following code examples:
// App.jsx
const myConfig = { ... }; // SDK configuration object
export const App = () => {
return (
<SplitFactoryProvider config={myConfig} >
<MyComponentWithFeatureFlags />
</SplitFactoryProvider>
)
};
// server.jsx
import { renderToString } from 'react-dom/server';
import { App } from './App';
app.use('/', (request, response) => {
const reactHtml = renderToString(<App />);
response.send(`
<!DOCTYPE html>
<html>
<head><title>My App</title></head>
<body><div id="root">${reactHtml}</div></body>
</html>
<script src="/client.js" async=""></script>
`);
});
// client.jsx, bundled into client.js
import { hydrateRoot } from 'react-dom/client';
import { App } from './App'
const domNode = document.getElementById('root');
const root = hydrateRoot(domNode, <App />);
// pages/index.jsx
export const getServerSideProps = (async () => {
...
const myConfig = { ... } // SDK configuration object
return { props: { myConfig, ... } }
});
export default function Page(props) {
return (
<SplitFactoryProvider config={props.myConfig} >
<MyComponentWithFeatureFlags />
</SplitFactoryProvider>
);
};
// SDK components are traditional "Client" components, so you need to use the 'use client' directive to nest with Server components
// https://nextjs.org/docs/app/building-your-application/rendering/client-components
// app/providers.jsx
'use client'
import { SplitFactoryProvider } from '@splitsoftware/splitio-react';
export default function Providers({ children }) {
const [myConfig] = React.useState(() => {
return { ... } // SDK configuration object
});
return (
<SplitFactoryProvider config={myConfig}>{children}</SplitFactoryProvider>
);
};
// app/layout.jsx
import Providers from './providers';
export default function RootLayout({ children }) {
return (
<html lang='en'>
<head />
<body>
<Providers>{children}</Providers>
</body>
</html>
);
};
// app/page.jsx
import { MyComponentWithFeatureFlags } from './MyComponentWithFeatureFlags';
export default async function Home() {
const props = await getAsyncData();
return (
<MyComponentWithFeatureFlags {...props} />
);
}
// app/MyComponentWithFeatureFlags.jsx
'use client'
import { useSplitTreatments } from '@splitsoftware/splitio-react';
const FEATURE_FLAG_NAME = 'test_split';
export const MyComponentWithFeatureFlags = (props) => {
const { treatments, isReady } = useSplitTreatments({ names: [FEATURE_FLAG_NAME] });
return isReady ?
treatments[FEATURE_FLAG_NAME].treatment === 'on' ?
<OnComponent {...props} /> :
<OffComponent {...props} /> :
<Loading {...props} />
};
Usage with React Native SDK
The Split React SDK can be used in React Native applications by combining it with the React Native SDK. For that, you need to instantiate a factory with the React Native SDK and pass it to the React SDK SplitFactoryProvider
component. The React SDK will use the factory to create the client and evaluate the feature flags.
import { SplitFactory } from '@splitsoftware/splitio-react-native';
import { SplitFactoryProvider } from '@splitsoftware/splitio-react';
const reactNativeFactory = SplitFactory({
core: {
authorizationKey: 'YOUR_CLIENT_SIDE_SDK_KEY',
key: 'key'
},
...
});
// Using the SplitFactoryProvider component, MyApp component and all it's descendants will have access to the SDK functionality using the provided React Native SDK factory.
const App = () => (
<SplitFactoryProvider factory={reactNativeFactory} >
<MyApp />
</SplitFactoryProvider>
);
Example apps
The following are example applications showing how you can integrate the React SDK into your code.
Comments
1 comment
Truly React Framework provides rich features and functionalities that helps in developing appealing and unique User Interfaces. React Framework is the crucial Development efforts put in to create attractive as well as soothing User Interface. Can you please help me with- "How different is React’s ES6 syntax when compared to ES5?"
Please sign in to leave a comment.