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

React SDK quickstart

Welcome to the quickstart guide for Optimizely Feature Experimentation's React SDK.

Follow the steps in this guide to create a feature flag, roll out a flag delivery, and run an A/B test using a simple command-line application.

Part 1: Create a sample app

1. Get a free account

You need an Optimizely account to follow this guide. If you do not have an account, you can register for a free account. If you already have an account navigate to your Feature Experimentation project.

2. Get your SDK key

To find your SDK Key in your Optimizely project:

  1. Go to Settings > Primary Environment.
  2. Copy and save the SDK Key for your primary environment.

🚧

Important

Each environment has its own SDK key. Ensure you copy the correct key.

SDK key for primary environment

Click image to enlarge

3. Copy the sample code

To quickly try out the SDK:

  1. Create a new react app using Create React App
npx create-react-app optimizely-react-quickstart
cd optimizely-react-quickstart
  1. Install the Optimizely React SDK. You can do so easily with Yarn.
yarn add @optimizely/react-sdk

The React SDK is open source and available on GitHub.

  1. Copy the following code sample into src/App.js file. Make sure to replace <Your_SDK_Key> with the SDK key you found in a previous step.
import React, { useState } from 'react';
import { createInstance, OptimizelyProvider, useDecision } from '@optimizely/react-sdk';

const optimizelyClient = createInstance({ sdkKey: 'YOUR_SDK_KEY' });

function Pre(props) {
  return <pre style={{ margin: 0 }}>{props.children}</pre>;
}

function isClientValid() {
  return optimizelyClient.getOptimizelyConfig() !== null;
}

const userIds = [];
while (userIds.length < 10) {
  // To get rapid demo results, generate an array of random users. 
  // Each user always sees the same variation unless you reconfigure the flag rule.
  userIds.push((Math.floor(Math.random() * 999999) + 100000).toString());
}

function App() {
  const [hasOnFlag, setHasOnFlag] = useState(false);
  const [isDone, setIsDone] = useState(false);
  const [isClientReady, setIsClientReady] = useState(null);

  optimizelyClient.onReady().then(() => {
    setIsDone(true);
    isClientValid() && setIsClientReady(true);
  });

  let projectId = '{project_id}';
  if (isClientValid()) {
    const datafile = JSON.parse(optimizelyClient.getOptimizelyConfig().getDatafile());
    projectId = datafile.projectId;
  }

  return (
    <OptimizelyProvider
      optimizely={optimizelyClient}
      // Generally React SDK runs for one client at a time i.e for one user throughout the lifecycle.
      // You can provide the user Id here once and the SDK will memoize and reuse it throughout the application lifecycle.
      // For this example, we are simulating 10 different users so we will ignore this and pass override User IDs to the useDecision hook for demonstration purpose.
      user={{ id: 'default_user' }}
    >
      <pre>Welcome to our Quickstart Guide!</pre>
      {isClientReady && (
        <>
          {userIds.map(userId => (
            <Decision key={userId} userId={userId} setHasOnFlag={setHasOnFlag} />
          ))}
          <br />
          {!hasOnFlag && <FlagsOffMessage projectId={projectId} />}
        </>
      )}
      {isDone && !isClientReady && (
        <Pre>
          Optimizely client invalid. Verify in Settings -> Environments that you used the primary environment's SDK key
        </Pre>
      )}
    </OptimizelyProvider>
  );
}

function FlagsOffMessage({ projectId }) {
  const navLink = `https://app.optimizely.com/v2/projects/${projectId}/settings/implementation`;
  return (
    <div>
      <Pre>Flag was off for everyone. Some reasons could include:</Pre>
      <Pre>1. Your sample size of visitors was too small. Rerun, or increase the iterations in the FOR loop</Pre>
      <Pre>
        2. By default you have 2 keys for 2 project environments (dev/prod). Verify in Settings>Environments that you
        used the right key for the environment where your flag is toggled to ON.
      </Pre>
      <Pre>
        Check your key at <a href={navLink}>{navLink}</a>
      </Pre>
      <br />
    </div>
  );
}

function Decision({ userId, setHasOnFlag }) {
  // Generally React SDK runs for one client at a time i.e for one user throughout the lifecycle.
  // You can provide the user Id once while wrapping the app in the Provider component and the SDK will memoize and reuse it throughout the application lifecycle.
  // For this example, we are simulating 10 different users so we will ignore this and pass override User IDs to the useDecision hook for demonstration purpose.
  // This override will not be needed for normal react sdk use cases.
  const [decision, clientReady] = useDecision('product_sort', {}, { overrideUserId: userId });

  // Don't render the component if SDK client is not ready yet.
  if (!clientReady) {
    return '';
  }

  const variationKey = decision.variationKey;

  // did decision fail with a critical error?
  if (variationKey === null) {
    console.log(' decision error: ', decision['reasons']);
  }

  if (decision.enabled) {
    setTimeout(() => setHasOnFlag(true));
  }

  // get a dynamic configuration variable
  // "sort_method" corresponds to a variable key in your Optimizely project
  const sortMethod = decision.variables['sort_method'];

  return (
    <Pre>
      {`\nFlag ${
        decision.enabled ? 'on' : 'off'
      }. User number ${userId} saw flag variation: ${variationKey} and got products sorted by: ${sortMethod} config variable as part of flag rule: ${
        decision.ruleKey
      }`}
    </Pre>
  );
}

export default App;
<!DOCTYPE html>
<html>
  <head>
    <title>Quickstart Guide</title>
    <script src="https://unpkg.com/@optimizely/optimizely-sdk/dist/optimizely.browser.umd.min.js"></script>
  </head>

  <body>
    <pre>Welcome to our Quickstart Guide!</pre>
    <pre id="errors"></pre>
    <pre id="experiences"></pre>
    <pre id="result"></pre>
    <script>
      var optimizelyClient = window.optimizelySdk.createInstance({
        sdkKey: '<YOUR_SDK_KEY>',
      });

      optimizelyClient.onReady().then(() => {
        var errors = document.getElementById('errors');
        if (!optimizelyClient.isValidInstance()) {
          errors.innerText =
            "Optimizely client invalid. Verify in Settings>Environments that you used the primary environment's SDK key";

          return;
        }

        var experiences = document.getElementById('experiences');
        let hasOnFlags = false;
        for (let i = 0; i < 10; i++) {
          // to get rapid demo results, generate random users. Each user always sees the same variation unless you reconfigure the flag rule.
          let userId = Math.floor(Math.random() * (10000 - 1000) + 1000).toString();

          // Create hardcoded user & bucket user into a flag variation
          let user = optimizelyClient.createUserContext(userId);

          // "product_sort" corresponds to a flag key in your Optimizely project
          let decision = user.decide('product_sort');
          let variationKey = decision.variationKey;

          // did decision fail with a critical error?
          if (variationKey === null) {
            errors.innerText += `\n\ndecision error: ${decision['reasons']}`;
          }

          // get a dynamic configuration variable
          // "sort_method" corresponds to a variable key in your Optimizely project
          let sortMethod = decision.variables['sort_method'];

          if (decision.enabled) {
            hasOnFlags = true;
          }

          // Mock what the users sees with print statements (in production, use flag variables to implement feature configuration)
          // always returns false until you enable a flag rule in your Optimizely project
          experiences.innerText += `\n\nFlag ${
            decision.enabled ? 'on' : 'off'
          }. User number ${user.getUserId()} saw flag variation: ${variationKey} and got products sorted by: ${sortMethod} config variable as part of flag rule: ${
            decision.ruleKey
          }`;
        }

        var result = document.getElementById('result');
        if (!hasOnFlags) {
          result.innerText =
            '\n\nFlag was off for everyone. Some reasons could include:' +
            '\n1. Your sample size of visitors was too small. Rerun, or increase the iterations in the FOR loop' +
            '\n2. By default you have 2 keys for 2 project environments (dev/prod). Verify in Settings>Environments that you used the right key for the environment where your flag is toggled to ON.' +
            '\n\nCheck your key at https://app.optimizely.com/v2/projects/' +
            optimizelyClient.projectConfigManager.getConfig().projectId +
            '/settings/implementation';
        }
      });
    </script>
  </body>
</html>

πŸ“˜

Note

Do not run your app yet, because you still need to set up the flag in the Optimizely app.

Part 2: Run your app

After completing Part 1, your app does nothing. You need to create a flag and a flag rule in the Optimizely app to enable the app.

1. Create the feature flag

A flag lets you control the users that are exposed to new code in your application. For this quickstart, imagine that you are rolling out a redesigned sorting feature for displaying products.

Create a flag in your Feature Experimentation project named product_sort and give it a variable named sort_method:

  1. Select Create New Flag... from the Flags tab.
  2. Enter product_sort in the Name field.
  3. Keep the auto-created Key, product_sort, and click Create Flag. The Key corresponds to the flag key in your sample app.

Next, create a variable in your flag:

  1. In your new flag, product_sort, under Flag Setupgo to Variables and click Add Variable (+).
  2. Select String in the Add Variable drop-down.
  1. Enter sort_method for the Variable Key, which corresponds to the variable key in your sample app.
  2. Enter alphabetical for the Default Value, which represents your old sorting method. The new sorting method is what you are rolling out.
  3. Click Save.
configure new string variable

Next, create a variation in your flag:

  1. Under Flag Setup go to Variations select the On variation. A variation is a wrapper for a collection of variable values.
  2. For the sort_method variable value, enter popular_first, which represents your new sorting method.
  3. Click Save.
Configure on variation

2. Create the flag delivery rule

Your sample app still does not do anything because you need to create and enable a flag rule.

Make a targeted delivery rule for the On variation for the product_sort flag. A targeted delivery lets you gradually release a feature flag to users, but with the flexibility to roll it back if you encounter bugs.

  1. Ensure you are in your primary environment (since you are using the primary environment SDK key from Part 1.
Verify you are in your production environment
  1. Click Add Rule and select Targeted Delivery.
  2. Enter Targeted Delivery for the Name field.
  3. Keep the default Key and Audiences.
  4. Set the Ramp Percentage traffic allocation slider to 50%. This delivers the product_sort flag to 50% of everyone who triggers the flag in this environment. You can roll out or roll back the product_sort flag to a percentage of traffic whenever you want.
  5. From the Deliver drop-down list, select the On variation.
  6. Click Save.
Add a targeted delivery rule delivering the on variation

3. Run your sample app

To run your sample application:

  1. Select Enable rule:
enable your rule
  1. Turn your flag On:
turn your flag on

Toggle your flag on or off

  1. Run the react application using yarn start. The output should appear in the browser and be similar to the following:
Flag on. User number 6998 saw flag variation: on and got products sorted by: popular_first config variable as part of flag rule: targeted_delivery

Flag on. User number 1177 saw flag variation: on and got products sorted by: popular_first config variable as part of flag rule: targeted_delivery

Flag on. User number 9714 saw flag variation: on and got products sorted by: popular_first config variable as part of flag rule: targeted_delivery

Flag on. User number 4140 saw flag variation: on and got products sorted by: popular_first config variable as part of flag rule: targeted_delivery

Flag on. User number 4994 saw flag variation: on and got products sorted by: popular_first config variable as part of flag rule: targeted_delivery

Flag off. User number 8700 saw flag variation: off and got products sorted by: alphabetical config variable as part of flag rule: default-rollout-208-19963693913

Flag off. User number 9912 saw flag variation: off and got products sorted by: alphabetical config variable as part of flag rule: default-rollout-208-19963693913

Flag on. User number 6560 saw flag variation: on and got products sorted by: popular_first config variable as part of flag rule: targeted_delivery

Flag on. User number 9252 saw flag variation: on and got products sorted by: popular_first config variable as part of flag rule: targeted_delivery

Flag on. User number 6582 saw flag variation: on and got products sorted by: popular_first config variable as part of flag rule: targeted_delivery

πŸ“˜

Note

You will not get exactly 50% of your user traffic in the "on" variation, since you are working with such small numbers of visitors. Also, the users who got an 'off' flag did not make it into the 50% traffic you set, so they fell through to the default "Off" rule (default-rollout in the preceding print statements).

4. How it works

So far, you:

  • Created a flag, flag variable, and a flag variation (wrapper for your variables) in your Optimizely project.
  • Implemented a flag in your app with the useDecision hook.

What is going on in your sample app?

How it works: decide to show a user a flag

The React SDK’s useDecision hook determines whether to show or hide the feature flag for a specific user.

πŸ“˜

Note

You can reuse this hook for different flag rules -- whether for delivering to more traffic, or running an experiment to show different sorting methods to just a portion of users.

After you learn which sorting method works best to increase sales, roll out the product sort flag to all traffic with the method set to the optimum value.

In your sample app:

// "product_sort" corresponds to the flag key you create in the Optimizely app
const [decision] = useDecision('product_sort');

πŸ“˜

Note

Optionally include attributes when you create your user (not shown in your sample app), so that you can target specific audiences. For example:

var attributes = { logged_in: true };

<OptimizelyProvider
  optimizely={optimizelyClient}        
	user={{
    id: 'userId',
    attributes: attributes,
  }}>
    { /*Application Components here*/ }
</OptimizelyProvider>

How it works: configure flag variations

You can dynamically configure a flag variation using flag variables. In your sample app:

// always returns false until you enable a flag rule in the Optimizely app
if (decision.enabled) 
{
  // "sort_method" corresponds to variable key you define in Optimizely app
 var sortMethod = decision.variables['sort_method'];
 console.log('sort_method: ', sortMethod);
}

For your product_sort flag, you can configure variations with different sort_method values, sorting by popular products, relevant products, promoted products, and so on. You can set different values for the sort method at any time in the Optimizely app.

Part 3: Run an A/B test

Part 2 of this tutorial guided you through a targeted delivery because it is the most straightforward flag rule. However, you often want to A/B test how users react to feature flag variations before you roll out a flag delivery.

  • Targeted delivery rule – You can roll out your flag to a percentage of your general user base (or to specific audiences), or roll back if you encounter bugs.
  • A/B test rule – Experiment by A/B testing a flag before you invest in delivering, so you know what to build. Track how users behave in flag variations, then interpret your experiment results using the Optimizely Stats Engine.

For Part 3, you will run an A/B test on the On variation of your product_sort flag.

1. Add event tracking

You need to add a Track Event method to your sample app, so you can mock up user events and then see metrics.

  1. Delete your old sample code, and paste in the following code.
  2. Replace your SDK key. See Get your SDK Key.
  3. Do not run your app yet because you still need to set up the A/B test in the Optimizely application.
import React, { useState } from 'react';
import { createInstance, OptimizelyProvider, useDecision } from '@optimizely/react-sdk';

const optimizelyClient = createInstance({ sdkKey: 'YOUR_SDK_KEY' });

function Pre(props) {
  return <pre style={{ margin: 0 }}>{props.children}</pre>;
}

function isClientValid() {
  return optimizelyClient.getOptimizelyConfig() !== null;
}

const userIds = [];
while (userIds.length < 4) {
  // to get rapid demo results, generate an array of random users. Each user always sees the same variation unless you reconfigure the flag rule.
  userIds.push((Math.floor(Math.random() * 999999) + 100000).toString());
}

let userMessages = userIds.reduce((result, userId) => ({ ...result, [userId]: [] }), {});
const donePromise = new Promise(resolve => {
  setTimeout(() => {
    optimizelyClient.onReady().then(() => {
      if (isClientValid()) {
        userIds.forEach(userId => {
          const question = `Pretend that user ${userId} made a purchase?`;
          const trackEvent = window.confirm(question);
          optimizelyClient.track('purchase', userId);
          const message = trackEvent
            ? 'Optimizely recorded a purchase in experiment results for user ' + userId
            : "Optimizely didn't record a purchase in experiment results for user " + userId;
          userMessages[userId].push(`${question} ${trackEvent ? 'Y' : 'N'}`, message);
        });
      }
      resolve();
    });
  }, 500);
});

function App() {
  const [hasOnFlag, setHasOnFlag] = useState(false);
  const [isDone, setIsDone] = useState(false);
  const [isClientReady, setIsClientReady] = useState(null);

  donePromise.then(() => setIsDone(true));
  optimizelyClient.onReady().then(() => {
    isClientValid() && setIsClientReady(true);
  });

  let projectId = '{project_id}';
  if (isClientValid()) {
    const datafile = JSON.parse(optimizelyClient.getOptimizelyConfig().getDatafile());
    projectId = datafile.projectId;
  }

  const reportsNavLink = `https://app.optimizely.com/v2/projects/${projectId}/reports`;

  return (
    <OptimizelyProvider
      optimizely={optimizelyClient}
      // Generally React SDK runs for one client at a time i.e for one user throughout the lifecycle.
      // You can provide the user Id here once and the SDK will memoize and reuse it throughout the application lifecycle.
      // For this example, we are simulating 10 different users so we will ignore this and pass override User IDs to the useDecision hook for demonstration purpose.
      user={{ id: 'default_user' }}
    >
      <pre>Welcome to our Quickstart Guide!</pre>
      {isClientReady && (
        <>
          {userIds.map(userId => (
            <>
              <Decision key={userId} userId={userId} setHasOnFlag={setHasOnFlag} />
              {userMessages[userId].map(message => (
                <Pre>{message}</Pre>
              ))}
              <br />
            </>
          ))}
          {!hasOnFlag && <FlagsOffMessage projectId={projectId} />}
          {isDone && (
            <>
              <Pre>Done with your mocked A/B test.</Pre>
              <Pre>
                Check out your report at <a href={reportsNavLink}>{reportsNavLink}</a>
              </Pre>
              <Pre>Be sure to select the environment that corresponds to your SDK key</Pre>
            </>
          )}
        </>
      )}
      {isDone && !isClientReady && (
        <Pre>
          Optimizely client invalid. Verify in Settings -> Environments that you used the primary environment's SDK key
        </Pre>
      )}
    </OptimizelyProvider>
  );
}

function FlagsOffMessage({ projectId }) {
  const navLink = `https://app.optimizely.com/v2/projects/${projectId}/settings/implementation`;
  return (
    <div>
      <Pre>Flag was off for everyone. Some reasons could include:</Pre>
      <Pre>1. Your sample size of visitors was too small. Rerun, or increase the iterations in the FOR loop</Pre>
      <Pre>
        2. By default you have 2 keys for 2 project environments (dev/prod). Verify in Settings>Environments that you
        used the right key for the environment where your flag is toggled to ON.
      </Pre>
      <Pre>
        Check your key at <a href={navLink}>{navLink}</a>
      </Pre>
      <br />
    </div>
  );
}

function Decision({ userId, setHasOnFlag }) {
  // Generally React SDK runs for one client at a time i.e for one user throughout the lifecycle.
  // You can provide the user Id once while wrapping the app in the Provider component and the SDK will memoize and reuse it throughout the application lifecycle.
  // For this example, we are simulating 10 different users so we will ignore this and pass override User IDs to the useDecision hook for demonstration purpose.
  // This override will not be needed for normal react sdk use cases.
  const [decision, clientReady] = useDecision('product_sort', {}, { overrideUserId: userId });

  // Don't render the component if SDK client is not ready yet.
  if (!clientReady) {
    return '';
  }

  const variationKey = decision.variationKey;

  // did decision fail with a critical error?
  if (variationKey === null) {
    console.log(' decision error: ', decision['reasons']);
  }

  if (decision.enabled) {
    setTimeout(() => setHasOnFlag(true));
  }

  // get a dynamic configuration variable
  // "sort_method" corresponds to a variable key in your Optimizely project
  const sortMethod = decision.variables['sort_method'];

  return (
    <Pre>
      {`Flag ${
        decision.enabled ? 'on' : 'off'
      }. User number ${userId} saw flag variation: ${variationKey} and got products sorted by: ${sortMethod} config variable as part of flag rule: ${
        decision.ruleKey
      }`}
    </Pre>
  );
}

export default App;

2. Pause other rules in free accounts

If you have a free Optimizely account, you need to pause the Targeted Delivery you created in Part 2 before you create your A/B test:

  1. Select the Flag that contains the Targeted Delivery you created in Part 2 from the Flags tab.
  2. Select the primary environment and the Targeted Delivery rule you created in Part 2.
  3. Click Disable Rule.
disable targeted delivery rule

3. Create the A/B test

To create an A/B Test rule in your Feature Experimentation project, in the flag you created in Part 2:

  1. Click Add Rule and select A/B Test.
select a b test rule
  1. Enter Experiment for the Name field.
  2. Keep the default Key and Audiences
  3. Keep the Ramp Percentage traffic allocation slider set to 100%.

4. Add an event

In an experiment, you track users' relevant actions to measure how they react to your flag variations. To define the actions you want to track, called events:

  1. Click on the Metrics field.
  2. Click Create new event.
add new event under metrics
  1. Enter purchase for the Event Name, and the Event Key will be automatically filled.
  2. (Optional) Enter a Description. You will want to know whether the new sorting flag helps customers figure out what to buy, so track whether the user makes a purchase after they were shown the products in a new order.
  3. Click Create Event.
create event
  1. In the Add Metric modal, leave the defaults, measure increase in unique conversions.
add metric that measures the increase in unique conversions
  1. Click Add Metric.
  2. Leave the default Off variation as a control. Select the On variation you configured in Part 2:
select the on variation

πŸ“˜

Note

You are not limited to two variations; you can also create A/B tests with multiple variations.

  1. Click Save to create your A/B Test rule.
  2. Select Enable rule.
enable a/b test rule

5. Run the A/B test

Ensure your flag is On and the rule is enabled, so your experiment can run:

rule enabled and flag on

Run your React app using yarn start. Output appears in the browser similar to the following, along with the confirmation boxes giving you the option to pretend to purchase or not:

Flag on. User number 103512 saw flag variation: on and got products sorted by: popular_first config variable as part of flag rule: experiment

Flag off. User number 134981 saw flag variation: off and got products sorted by: alphabetical config variable as part of flag rule: experiment

Flag on. User number 254103 saw flag variation: on and got products sorted by: popular_first config variable as part of flag rule: experiment

Flag on. User number 736927 saw flag variation: on and got products sorted by: popular_first config variable as part of flag rule: experiment

The confirmation boxes, asking to pretend to purchase, appear similar to the following:

Once the selections are made for each user, the final output appears similar to the following:

Welcome to our Quickstart Guide!
Flag on. User number 103512 saw flag variation: on and got products sorted by: popular_first config variable as part of flag rule: experiment
Pretend that user 103512 made a purchase? N
Optimizely didn't record a purchase in experiment results for user 103512

Flag off. User number 134981 saw flag variation: off and got products sorted by: alphabetical config variable as part of flag rule: experiment
Pretend that user 134981 made a purchase? N
Optimizely didn't record a purchase in experiment results for user 134981

Flag on. User number 254103 saw flag variation: on and got products sorted by: popular_first config variable as part of flag rule: experiment
Pretend that user 254103 made a purchase? N
Optimizely didn't record a purchase in experiment results for user 254103

Flag on. User number 736927 saw flag variation: on and got products sorted by: popular_first config variable as part of flag rule: experiment
Pretend that user 736927 made a purchase? N
Optimizely didn't record a purchase in experiment results for user 736927

Done with your mocked A/B test.
Check out your report at https://app.optimizely.com/v2/projects/20130771383/reports
Be sure to select the environment that corresponds to your SDK key

6. See your A/B test results

Go to the Reports tab and select your experiment to see your results.

results page

Your results should look similar:

πŸ“˜

Note

  • You might not see the exact user traffic percentages you configured for your flag variations until you have larger numbers of users.
  • You might not see your user traffic immediately. Refresh the browser to refresh traffic.
  • Your experiment results will not tell you a winning variation until you have a large number of visitors, (on the order of 100,000).

7. How it works

For an A/B test, you need a way to tell Optimizely when a user made a purchase in your app and map this event in your app code to the specific event you created in Optimizely. Luckily the SDK has a method for that! Use the Track Event method and pass in the key for the event you created (purchase). In your sample app:

// Track how users behave when they see a flag variation
// e.g., after your app processed a purchase, let Optimizely know what happened:
optimizelyClient.track('purchased');

πŸ“˜

Note

Optionally add tags to your event to enrich it (not shown in your sample app). You can also use reserve tag keys like revenue to track quantitative results. For example:

var tags = {
  category: 'shoes',
  revenue: 6432,
};

optimizelyClient.track('purchase', null, tags);

Conclusion

Congratulations! You successfully set up and launched your first Optimizely Feature Experimentation experiment. While this example focused on optimizing sales, Optimizely’s experimentation platform can support an open-ended set of experimentation use cases.

See our complete React SDK documentation to learn more ways to optimize your software using experimentation.