React

This SDK wraps the JavaScript SDK for smoother integration with React applications. However, if you're building a non-React application for web browsers, you should use our JavaScript SDK instead.

  • @flagsync/react-sdk relies on the React Context API (React 16.3+).

  • TypeScript is fully supported.

Get your SDK key

To start, you'll first need to locate your SDK key, which is specific to an Environment (e.g. Test, Production, Staging, etc.)

SDK keys are found within your organization's workspace settings.

Your API requests are authenticated using SDK keys. Any request that doesn't include an SDK key will return an error.

Client-side SDK keys are safe to expose, and should be used for client-facing applications such as Web Browsers, whereas server-side SDKs can expose details of your flag rules, such as internal email addresses or user keys, and should never be made public.

Install the library

To connect to FlagSync from code, you'll need to install the SDK.

# npm
npm install @flagsync/react-sdk

# yarn
yarn add @flagsync/react-sdk

# pnpm
pnpm add @flagsync/react-sdk

Getting started

Here's a simple example that shows the most basic usage of the SDK:

import { useFlag, FlagSyncProvider, FsConfig } from "@flagsync/react-sdk";

const config: FsConfig = {
  sdkKey: 'YOUR_SDK_KEY',
  core: {
    key: 'USER_ID',
  },
}

function MyComponent() {
  const { value, isReady } = useFlag<string>('my-flag');

  if (!isReady) {
    return <span>Loading...</span>;
  }

  return <span>{value}</span>;
}

function MyApp() {
  return (
    <FlagSyncProvider config={config}>
      <MyComponent />
    </FlagSyncProvider>
  )
} 

Initialize the FlagSync client

To initialize the FlagSync client, you'll need your SDK key, and core.key, which is often a unique identifier, such as user ID or email, and pass it to FlagSyncProvider.

Similar to other context providers, you can place FlagSyncProvider at the root of your application, or as low in the component tree as you need, assuming your flag retrievals are below that depth.

const config: FsConfig = {...}

<FlagSyncProvider config={config}>
  {children}
</FlagSyncProvider>

Flag evaluation (hooks)

Flag impression(s) are automatically registered whenever the useFlag or useFlags hook is called from the SDK.

The SDK uses memoization to ensure that renders provoked from outside FlagSync do not result in duplicate impressions.

useFlag

  • The useFlag hook retrieves the value of a feature flag identified by the flagKey.

  • This function evaluates the user context against individual targeting rules, percentage rollouts, and default variants to determine and return the appropriate feature flag variant for that specific user.

If the SDK is not ready, either control will be returned, or the defaultValue if one is provided.

type UseFlag<T> = {
  value: T | 'control';
  isReady: boolean;
  isReadyFromStore: boolean;
};

useFlag<T>(flagKey: string, defaultValue?: T): UseFlag<T>

Example usage

function MyComponent() {
  const { value, isReady } = useFlag<string>('my-flag');

  if (!isReady) {
    return <span>Loading...</span>;
  }

  return <span>{value}</span>;
}

useFlags

  • The useFlags hook retrieves the values of all feature flags.

  • This function evaluates the user context against individual targeting rules, percentage rollouts, and default variants to determine and return the appropriate feature flag variants for that specific user.

If the SDK is not ready, either control will be returned, or the defaultValue if one is provided.

import { FsFlagSet } from '@flagsync/react-sdk';

export type UseFlags = {
  flags: FsFlagSet;
  isReady: boolean;
  isReadyFromStore: boolean;
};

useFlags(defaultValues: FsFlagSet = {}): UseFlags

Example usage

function MyComponent() {
  const { flags, isReady } = useFlags();

  if (!isReady) {
    return <span>Loading...</span>;
  }

  return <span>{flags['my-flag']}</span>;
}

Flag evaluation (component-based)

Instead of hooks, an alternative has been provided in the form of the render props pattern.

Flag impression(s) are automatically registered whenever the FlagSyncFlag or FlagSyncFlags component is used.

The SDK uses memoization to ensure that renders provoked from outside FlagSync do not result in duplicate impressions.

<FlagSyncFlag />

  • Identical to useFlag.

  • The FlagSyncFlag component retrieves the value of a feature flag identified by the flagKey.

  • This component evaluates the user context against individual targeting rules, percentage rollouts, and default variants to determine and return the appropriate feature flag variant for that specific user.

If the SDK is not ready, either control will be returned, or the defaultValue if one is provided.

type FlagSyncFlagProps<T> = {
  flagKey: string;
  defaultValue: T;
  children: ({
    value,
    isReady,
    isReadyFromStore,
  }: {
    value: T | 'control';
    isReady: boolean;
    isReadyFromStore: boolean;
  }) => ReactNode;
};

Example usage

const MyComponent = () => {
  return (
    <div>
      <span>Hello, World!</span>
      
      <FlagSyncFlag flagKey="my-flag">
        {({ value, isReady }) => {
          return !isReady 
              ? (<span>Loading...</span>) 
              : (<span>{value}</span>);
        }}
      </FlagSyncFlag>
      
    </div>   
  )
}

<FlagSyncFlags />

  • Identical to useFlags.

  • The FlagSyncFlags component retrieves the values of all feature flags.

  • This component evaluates the user context against individual targeting rules, percentage rollouts, and default variants to determine and return the appropriate feature flag variants for that specific user.

If the SDK is not ready, either control will be returned, or the defaultValue if one is provided.

import { FsFlagSet } from '@flagsync/react-sdk';

type FlagSyncFlagProps = {
  defaultValues?: FsFlagSet;
  children: ({
    flags,
    isReady,
    isReadyFromStore,
  }: {
    flags: FsFlagSet;
    isReady: boolean;
    isReadyFromStore: boolean;
  }) => ReactNode;
};

Example usage

const MyComponent = () => {
  return (
    <div>
      <span>Hello, World!</span>
      
      <FlagSyncFlags>
        {({ flags, isReady }) => {
          return !isReady ? (
            <span>Loading...</span>
          ) : (
            <span>{flags['my-flag']}</span>
          );
        }}
      </FlagSyncFlags>
      
    </div>   
  )
}

Submit events

Track user actions or events within the application with the useTrack hook.

Per the type definitions below, if you choose to pass an eventKey to useTrack you are effectively binding the return function that that specific event.

export function useTrack(): TrackFunction;
export function useTrack(eventKey: string): TrackFunctionWithEventKey;

export type TrackFunction = (
  eventKey: string,
  value?: number | null | undefined,
  properties?: Record<string, unknown> | undefined,
) => void;

export type TrackFunctionWithEventKey = (
  value?: number | null,
  properties?: Record<string, unknown>,
) => void;

Example usage

export const RegisterButton = () => {
  const { value } = useFlag<string>('register-button-text', 'Register');
  
  const track = useTrack();                    // generic track function
  const trackReg = useTrack('register-event'); // track function for "register-event"

  useEffect(() => {
    track('page-load-time', 1.234)
  }, [])

  return (
    <button
      onClick={() => {
        trackReg();
        // Register the user
      }}
    >
      {value}
    </button>
  );
};

When you call the track function to submit events, you can optionally provide key-value properties or a numeric eventValue.

For more information on the track function, check out the JavaScript SDK reference.


Flag updates

When a change to a feature flag is made in the FlagSync dashboard, a server-side event (SSE) is sent from FlagSync servers to the SDK.

Value change propagation is on the order of milliseconds.

Unlike the JavaScript SDK, there is no need to listen for events in the React SDK, any component using a useFlag or useFlags hook with re-render automatically, same with <FlagSyncFlag/> and <FlagSyncFlags />.

While the default synchronization method is SSE, you may opt to use polling instead, or disable synchronization entirely. You can control this with sync option

import { FsConfig, SyncType } from "@flagsync/react-sdk";

type SyncType = 'stream' | 'poll' | 'off'

const config: FsConfig = {
  sdkKey: 'YOUR_SDK_KEY',
  core: {
    key: string
  },
  sync?: {
    type?: SyncType;
    pollInterval?: number;
  };
};

Custom Attributes

When initializing the config, you can pass custom attributes about the user or user segment, which are automatically made available in the FlagSync Dashboard for individual targeting.

import { FsConfig, SyncType } from "@flagsync/react-sdk";

const config: FsConfig = {
 sdkKey: 'YOUR_SDK_KEY',
  core: {
    key: 'userId_0x123',
    attributes: {
      {
        "email": "user@example.com"
        "department: "QA Test"
        "location": "Boston
      }
    },
  },
});

Create a targeting rule based on the group attribute.


Bootstrapping

You can initialize the SDK with a set of flags using the bootstrap configuration. This is useful for setting default values or for testing.

import { FsConfig } from "@flagsync/react-sdk";

/**
 *   Remote configuration:
 *   {
 *     "my-flag": "value-1"
 *   }
 */

const config: FsConfig = {
    sdkKey: 'YOUR_SDK_KEY',
    core: {
      key: 'userId_0x123'
    },
    bootstrap: {
      'my-flag': 'value-2'
    }
}

useFlag<string>('my-flag'); // { isReady: false, value: 'value-2' }

// SDK is now ready...

useFlag<string>('my-flag'); // { isReady: true, value: 'value-1' }

Storage

By default, flags are stored in memory, however when running in a Web Browser, you can opt to use LocalStorage instead, which is useful for quickly loading the last state of your flags.

import { StorageType} from "@flagsync/react-sdk";

type StorageType = 'memory' | 'localstorage'

const factory = FlagSyncFactory({
  sdkKey: 'YOUR_SDK_KEY',
  core: {
    key: string
  },
  storage?: {
    type?: StorageType;
    prefix?: string;
  };
});

When initializing from LocalStorage, the isReadyFromStore value for useFlag, useFlags, <FlagSyncFlag />, and <FlagSyncFlags /> will be true once the flags have been loaded.

function MyComponent() {
  const { value, isReady, isReadyFromStore } = useFlag<string>('my-flag');

  if (isReady) {
    return <span>{value}</span>;
  }

  if (isReadyFromStore) {
    return <span>{value}</span>;
  }

  return <span>Loading...</span>;
}

Occurs quickly since no network request is required.

This data may be stale, and should not be considered a replacement for the isReady boolean.


FsConfig

You can configure any of the following option arguments when initializing the config object for <FlagSyncProvider />.

type FsFlagValue = any;
export type FsFlagSet = Record<string, FsFlagValue>;

type CustomAttributeValue = any;
type CustomAttributes = Record<string, CustomAttributeValue>;

type StorageType = 'memory' | 'localstorage';
type SyncType = 'stream' | 'poll' | 'off';

export interface FsConfig {
  /**
   * The SDK key for the FlagSync project.
   */
  readonly sdkKey: string;
  readonly core: {
    /**
     * The unique key for the user, or organization.
     */
    key: string;
    /**
     * The custom attributes for the user, or organization, which are
     * made available for rule targeting in your FlagSync dashboard.
     * > CustomAttributes: Record<string, CustomAttributeValue>
     *   Example: 
     *     {
     *       "email": "user@example.com"
     *       "jobTitle: "QA"
     *       "location": "Boston
     *     }     
     */
    attributes?: CustomAttributes;
  };
  /**
   * Initialize the SDK with a set of flags.
   * > FsFlagSet: Record<string, FsFlagValue>;
   */
  readonly bootstrap?: FsFlagSet;
  /**
   * The storage configuration for the SDK.
   * > StorageType: 'memory' | 'localstorage'
   */
  readonly storage?: {
    type?: StorageType;
    prefix?: string;
  };
  /**
   * The configuration for the SDK's sync mechanism.
   * > SyncType: 'stream' | 'poll' | 'off';
   */
  readonly sync?: {
    type?: SyncType;
    pollRate?: number;
  };
  readonly tracking?: {
    impressions?: {
      maxQueueSize: number;
      pushRate: number;
    };
    events?: {
      maxQueueSize: number;
      pushRate: number;
    };
  };
  /**
   * The configuration for the SDK's logging.
   * > LogLevel: 'DEBUG' | 'INFO' | 'WARN' | 'ERROR' | 'NONE';
   */
  readonly logLevel?: LogLevel;
}

Last updated