Skip to main content
  1. All Posts/

react-ab-test

Tools JavaScript

A/B Testing React Components






Wrap components in <Variant /> and nest in <Experiment />. A variant is chosen randomly and saved to local storage.

<Experiment name="My Example">
  <Variant name="A">
    <div>Version A</div>
  </Variant>
  <Variant name="B">
    <div>Version B</div>
  </Variant>
</Experiment>

Report to your analytics provider using the emitter. Helpers are available for Mixpanel and Segment.com.

emitter.addPlayListener(function(experimentName, variantName){
  mixpanel.track("Start Experiment", {name: experimentName, variant: variantName});
});

Please ★ on GitHub!

Table of Contents

  • Installation
  • Usage

    • Standalone Component
    • Coordinate Multiple Components
    • Weighting Variants
    • Debugging
    • Server Rendering

    •   <li>
          <a rel="nofollow noopener" target="_blank" href="#with-babel">With Babel</a>
        </li>
      </ul>
      
    • Alternative Libraries
    • Resources for A/B Testing with React
    • API Reference

      Installation

      react-ab-test is compatible with React 0.14.x and 0.15.x.

      npm install react-ab-test

      Usage

      Standalone Component

      Try it on JSFiddle

      var Experiment = require("react-ab-test/lib/Experiment");
      var Variant = require("react-ab-test/lib/Variant");
      var emitter = require("react-ab-test/lib/emitter");
      
      var App = React.createClass({
        onButtonClick: function(e){
          this.refs.experiment.win();
        },
        render: function(){
          return <div>
            <Experiment ref="experiment" name="My Example">
              <Variant name="A">
                <div>Section A</div>
              </Variant>
              <Variant name="B">
                <div>Section B</div>
              </Variant>
            </Experiment>
            <button onClick={this.onButtonClick}>Emit a win</button>
          </div>;
        }
      });
      
      // Called when the experiment is displayed to the user.
      emitter.addPlayListener(function(experimentName, variantName){
        console.log("Displaying experiment ‘" + experimentName + "’ variant ‘" + variantName + "’");
      });
      
      // Called when a 'win' is emitted, in this case by this.refs.experiment.win()
      emitter.addWinListener(function(experimentName, variantName){
        console.log("Variant ‘" + variantName + "’ of experiment ‘" + experimentName + "’  was clicked");
      });

      Coordinate Multiple Components

      Try it on JSFiddle

      var Experiment = require("react-ab-test/lib/Experiment");
      var Variant = require("react-ab-test/lib/Variant");
      var emitter = require("react-ab-test/lib/emitter");
      
      // Define variants in advance.
      emitter.defineVariants("My Example", ["A", "B", "C"]);
      
      var Component1 = React.createClass({
        render: function(){
          return <Experiment name="My Example">
            <Variant name="A">
              <div>Section A</div>
            </Variant>
            <Variant name="B">
              <div>Section B</div>
            </Variant>
          </Experiment>;
        }
      });
      
      var Component2 = React.createClass({
        render: function(){
          return <Experiment name="My Example">
            <Variant name="A">
              <div>Subsection A</div>
            </Variant>
            <Variant name="B">
              <div>Subsection B</div>
            </Variant>
            <Variant name="C">
              <div>Subsection C</div>
            </Variant>
          </Experiment>;
        }
      });
      
      var Component3 = React.createClass({
        onButtonClick: function(e){
          emitter.emitWin("My Example");
        },
        render: function(){
          return <button onClick={this.onButtonClick}>Emit a win</button>;
        }
      });
      
      var App = React.createClass({
        render: function(){
          return <div>
            <Component1 />
            <Component2 />
            <Component3 />
          </div>;
        }
      });
      
      // Called when the experiment is displayed to the user.
      emitter.addPlayListener(function(experimentName, variantName){
        console.log("Displaying experiment ‘" + experimentName + "’ variant ‘" + variantName + "’");
      });
      
      // Called when a 'win' is emitted, in this case by emitter.emitWin()
      emitter.addWinListener(function(experimentName, variantName){
        console.log("Variant ‘" + variantName + "’ of experiment ‘" + experimentName + "’ was clicked");
      });

      Weighting Variants

      Try it on JSFiddle
      Use emitter.defineVariants() to optionally define the ratios by which variants are chosen.

      var Experiment = require("react-ab-test/lib/Experiment");
      var Variant = require("react-ab-test/lib/Variant");
      var emitter = require("react-ab-test/lib/emitter");
      
      // Define variants and weights in advance.
      emitter.defineVariants("My Example", ["A", "B", "C"], [10, 40, 40]);
      
      var App = React.createClass({
        render: function(){
          return <div>
            <Experiment ref="experiment" name="My Example">
              <Variant name="A">
                <div>Section A</div>
              </Variant>
              <Variant name="B">
                <div>Section B</div>
              </Variant>
              <Variant name="C">
                <div>Section C</div>
              </Variant>
            </Experiment>
          </div>;
        }
      });

      Debugging

      The debugger attaches a fixed-position panel to the bottom of the <body> element that displays mounted experiments and enables the user to change active variants in real-time.
      The debugger is wrapped in a conditional if(process.env.NODE_ENV === "production") {...} and will not display on production builds using envify.

      Try it on JSFiddle

      var Experiment = require("react-ab-test/lib/Experiment");
      var Variant = require("react-ab-test/lib/Variant");
      var experimentDebugger = require("react-ab-test/lib/debugger");
      
      experimentDebugger.enable();
      
      var App = React.createClass({
        render: function(){
          return <div>
            <Experiment ref="experiment" name="My Example">
              <Variant name="A">
                <div>Section A</div>
              </Variant>
              <Variant name="B">
                <div>Section B</div>
              </Variant>
            </Experiment>
          </div>;
        }
      });

      Server Rendering

      A <Experiment /> with a userIdentifier property will choose a consistent <Variant /> suitable for server side rendering.
      See ./examples/isomorphic for a working example.

      Example

      The component in Component.jsx:

      var React = require("react");
      var Experiment = require("react-ab-test/lib/Experiment");
      var Variant = require("react-ab-test/lib/Variant");
      
      module.exports = React.createClass({
        propTypes: {
          userIdentifier: React.PropTypes.string.isRequired
        },
        render: function(){
          return <div>
            <Experiment ref="experiment" name="My Example" userIdentifier={this.props.userIdentifier}>
              <Variant name="A">
                <div>Section A</div>
              </Variant>
              <Variant name="B">
                <div>Section B</div>
              </Variant>
            </Experiment>
          </div>;
        }
      });

      We use a session ID for the userIdentifier property in this example, although a long-lived user ID would be preferable. See server.js:

      require("babel/register")({only: /jsx/});
      
      var express = require('express');
      var session = require('express-session');
      var React = require("react");
      var ReactDOMServer = require("react-dom/server");
      var Component = require("./Component.jsx");
      var abEmitter = require("react-ab-test/lib/emitter")
      
      var app = express();
      
      app.set('view engine', 'ejs');
      
      app.use(session({
        secret: 'keyboard cat',
        resave: false,
        saveUninitialized: true
      }));
      
      app.get('/', function (req, res) {
        var reactElement = React.createElement(Component, {userIdentifier: req.sessionID});
        var reactString = ReactDOMServer.renderToString(reactElement);
        abEmitter.rewind();
        res.render('template', {
          sessionID: req.sessionID,
          reactOutput: reactString
        });
      });
      
      app.use(express.static('www'));
      
      app.listen(8080);

      Remember to call abEmitter.rewind() to prevent memory leaks.
      An EJS template in template.ejs:

      <!doctype html>
      <html>
        <head>
          <title>Isomorphic Rendering Example</title>
        </head>
        <script type="text/javascript">
          var SESSION_ID = <%- JSON.stringify(sessionID) %>;
        </script>
        <body>
          <div id="react-mount"><%- reactOutput %></div>
          <script type="text/javascript" src="bundle.js"></script>
        </body>
      </html>

      On the client in app.jsx:

      var React = require('react');
      var ReactDOM = require('react-dom');
      var Component = require("../Component.jsx");
      
      var container = document.getElementById("react-mount");
      
      ReactDOM.render(<Component userIdentifier={SESSION_ID} />, container);

      With Babel

      Code from ./src is written in JSX and transpiled into ./lib using Babel. If your project uses Babel you may want to include files from ./src directly.

      Alternative Libraries

      • react-experiments – “A JavaScript library that assists in defining and managing UI experiments in React” by Hubspot. Uses Facebook’s PlanOut framework via Hubspot’s javascript port.
      • react-ab – “Simple declarative and universal A/B testing component for React” by Ola Holmström
      • react-native-ab – “A component for rendering A/B tests in React Native” by Loch Wansbrough

      Please let us know about alternate libraries not included here.

      Resources for A/B Testing with React

      Please let us know about React A/B testing resources not included here.

      API Reference

      <Experiment />

      Experiment container component. Children must be of type Variant.

      • Properties:

        • name – Name of the experiment.

          • Required
          • Type: string
          • Example: "My Example"
        •   <li>
              <code>userIdentifier</code> &#8211; Distinct user identifier. When defined, this value is hashed to choose a variant if&#8230;</code>
            </li>
          </ul>