Unlock seamless workflows and faster delivery with our latest releases – get the details

Feature flags with Sanity and Next.js

By Jan Hoogeveen

Feature flags with Sanity and Next.js

feature-flags.js (Schema)

export default {
  name: 'featureFlags',
  type: 'document',
  title: 'Feature flags',
  fields: [
    {
      title: 'Feature flags',
      name: 'flags',
      description:
        'Use feature flags to quickly enable or disable (new) features on production.',
      type: 'array',
      of: [
        {
          title: 'Feature',
          name: 'feature',
          type: 'object',
          fields: [
            {
              name: 'title',
              type: 'string',
              title: 'Title',
            },
            {
              name: 'key',
              type: 'string',
              title: 'The key used to turn off/on features in the front-end',
            },
            {
              name: 'description',
              type: 'text',
              title: 'Description',
              description:
                'Description of the feature',
            },
            {
              name: 'status',
              type: 'boolean',
              description:
                'Disable or enable the feature',
              title: 'Enabled / disabled?',
            },
          ],
        },
      ],
    },
  ],
};

_app.js

function createProviderFlags(flags) {
  const filtered = flags
    .filter((feature) => feature?.status === true)
    .map((feature) => feature.key);
  return filtered ? filtered : [];
}


export default function App(props) {
  const { Component, pageProps } = props;
  const [featureflags, setFeatureFlags] = useState(undefined);
  useEffect(() => {
    // fetch the feature flags on page load
    client.fetch('*[_type == "featureFlags"]').then((featureflags) => {
      const flags = createProviderFlags(featureflags);
      setFeatureFlags(flags);
    });
    
    // use a subscription to listen to changes in real-time
    // keep an eye on your API limits though!
    // you can safely remove this whole subscription if you're
    // using ISR
    const subscription = client.listen(query).subscribe((update) => {
      if (update?.result?.flags) {
        const flags = createProviderFlags(update.result.flags);
        setFeatureFlags(flags);
      }
    });
    return () => {
      subscription.unsubscribe();
    };
  }, []);
  
  return (
    <FeatureProvider features={featureflags}>
      <Component {...pageProps} />
    </FeatureProvider>
  );
}

feature-flags.jsx (Context and hook)

import { createContext, useContext, ReactNode } from 'react';

export const FeatureFlagsContext = createContext(undefined);

export function FeatureProvider({ features = [], children }) {
  return (
    <FeatureFlagsContext.Provider value={features}>
      {children}
    </FeatureFlagsContext.Provider>
  );
}

export function useFeature(name) {
  const features = useContext(FeatureFlagsContext);
  return features.includes(name);
}

Feature flags are great for when you're developing new features and you're uncertain of when they should go live. It could be tomorrow, it could be in a week. By wrapping new features in a feature flag you can merge new features in code that gets deployed to production, even if you're unsure if it's going to make the release.

By storing your feature flags in Sanity you can toggle features on or off in the CMS. You can combine this with Next.js' Incremental Static Regeneration to toggle features without having to redeploy.

This example shows how to use a listener to switch feature flags in realtime.

Contributor

Other schemas by author