This article is more than 10 months old
HomeFeature flags with React Hooks

Feature flags with React Hooks

April 30, 20234 min read

Feature flags are boolean values that we can use to conditionally show or hide parts of our application. This blog currently uses feature flags to hide incomplete articles and pages that are still being worked on.

Much has been written about the benefits of using feature flags. You can check out this article for a good summary of their benefits. In this post, I'll walk you through building a minimal feature flagging system using React hooks.

Storing the feature flags

First, we will store all the information about our features as a JSON object:

{
  "feature1": {
    "environments": {
      "production": {
        "enabled": false
      },
      "development": {
        "enabled": false
      }
    }
  },
  "feature2": {
    "environments": {
      "production": {
        "enabled": true
      },
      "development": {
        "enabled": true
      }
    }
  }
}

Each feature has an environment property, which is an object containing two properties: development and production. Each of these properties contains an additional object with a single property, enabled, which is a boolean value. We can use this to enable or disable a particular feature depending on the environment that the application is currently in.

Checking for a feature with Hooks

Before building the hooks, let's create TypeScript types for our features.json. We need a Feature type that is a union of all the keys in our features object:

//our json object
import features from "./features.json";

//A union of the keys (features) in our json object
type Feature = keyof typeof features;

Next, we need to define a FeatureConfig type, which specifies the environments and features that are enabled:

type FeatureConfig = {
  environments: {
    production: {
      enabled: boolean;
    };
    development: {
      enabled: boolean;
    };
  };
};

Finally, we combine the two types using the TypeScript Record utility to create the FeatureSet type. This type represents the entire features.json:

type FeatureSet = Record<Feature, FeatureConfig>;

We will build two hooks to check if a feature is enabled. The first hook, useFeatureCheck, returns a function that takes in a feature name as an argument and checks if that feature is enabled based on our features.json:

function useFeatureCheck() {
  return React.useCallback((feature: Feature) => {
    //get the feature config
    const featureConfig = features[feature];

    //check if feature has been configured
    if (!featureConfig) return false;

    const { environments } = featureConfig;

    //check for the environments the feature is enabled in
    switch (process.env.NODE_ENV) {
      case "production":
        return environments.production.enabled;
      case "development":
        return environments.development.enabled;
      default:
        return false;
    }
  }, []);
}

The second hook is the useFeatures hook. Within this hook, we loop over all the features and create a new object with the feature names as properties and their values booleans, indicating whether they are enabled or not. We will check if a feature is enabled using the function returned from the useFeatureCheck hook we built earlier:

export function useFeatures() {
  const checkFeature = useFeatureCheck();

  return React.useMemo(
    () =>
      Object.keys(features).reduce(
        (acc, feature) => ({
          ...acc,
          [feature]: checkFeature(feature),
        }),
        {} as Record<Feature, boolean>
      ),
    [checkFeature]
  );
}

We could also create another hook called useFeature that takes a feature name as an argument and returns a boolean indicating whether that feature is enabled or not:

export function useFeature(feature: Feature) {
  const checkFeature = useFeatureCheck();

  return React.useMemo(() => checkFeature(feature), [checkFeature, feature]);
}

Using the hooks

We can now use these hooks to conditionally render our components:

import { useFeatures } from "./useFeatures";

function Feature1() {
  return <div>Feature 1</div>;
}

function Feature2() {
  return <div>Feature 2</div>;
}

function App() {
  const features = useFeatures();

  return (
    <>
      {features.feature1 ? <Feature1 /> : null}
      {features.feature2 ? <Feature2 /> : null}
    </>
  );
}

I hope you enjoyed this post. If you have any questions, you can reach me on Twitter @kxlaa_ and I'll be happy to answer them.

Thank you for reading and happy coding!