Skip to main content

React SDK

React compatibility

Use matching React and React DOM versions: React 18.2+ or React 19.x (^18.2.0 || ^19.0.0). The SDK shares your application's React and JSX runtimes through peer dependencies. Existing React 18 applications can keep their React 18 setup. See the support requirements for the peer range.

Start with the React SDK sample. Read its README, then work through its home, gates, programmatic API, identity, Order context, and filter examples in order. Use the Samples catalog to find the other runnable SDK examples.

Use Toggly's React SDK in React applications.

Installation​

Install the React feature flags package using NPM:

$ npm i -s @ops-ai/react-feature-flags-toggly

React and React DOM are installed by the host application. Keep their versions matched; for a new React 19 application:

npm install react@19 react-dom@19 @ops-ai/react-feature-flags-toggly
npm install --save-dev @types/react@19 @types/react-dom@19

For an existing React 18 application, retain React/React DOM 18.2 or later in the 18.x line and matching React 18 type packages. The provider and component setup below is the same for both supported majors.

Basic Usage (with Toggly.io)​

Setup Provider​

Import createTogglyProvider in your index file:

import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import { createTogglyProvider } from '@ops-ai/react-feature-flags-toggly'

Create a TogglyProvider with your App Key & Environment name from your Toggly application page:

const TogglyProvider = await createTogglyProvider({
appKey: 'your-app-key', // You can find this in Toggly.io
environment: 'your-environment-name', // You can find this in Toggly.io
onError: (message, error) => {
// Report fetch/cache/refresh failures to your monitoring provider
console.warn('Toggly error:', message, error)
},
})

Wrap your App component with the newly created TogglyProvider:

const root = createRoot(
document.getElementById('root') as HTMLElement,
)
root.render(
<StrictMode>
<TogglyProvider>
<App />
</TogglyProvider>
</StrictMode>,
)

Using the Feature Component​

Now you can start using the Feature component anywhere in your application by importing it:

import { Feature } from '@ops-ai/react-feature-flags-toggly'
<Feature featureKey={'firstFeature'}>
<p>This feature can be turned on or off.</p>
</Feature>

Feature Component Options​

You can also check multiple feature keys and make use of the requirement (all/any) and negate (bool) options (requirement is set to "all" by default).

Show if all features are on​

<Feature featureKeys={['firstFeature', 'secondFeature']}>
<p>ALL the provided feature keys are TRUE.</p>
</Feature>

Show if any feature is on​

<Feature featureKeys={['firstFeature', 'secondFeature']} requirement={'any'}>
<p>AT LEAST ONE the provided feature keys is TRUE.</p>
</Feature>

Show if features are off (negate)​

<Feature featureKeys={['firstFeature', 'secondFeature']} negate={true}>
<p>NONE of the provided feature keys is TRUE.</p>
</Feature>

Feature vs conditional UI​

NeedUse
Remove content from the DOM when off<Feature> children, or a second <Feature negate>
Keep content mounted; drive CSS, disabled state, or handlers<Feature render={...} />, useFeatureFlag, or useFeatureGate

Use <Feature> when you only need show/hide. When you need styling, taps, or behavior driven by the resolved gate boolean (while keeping content mounted), use the render prop or hooks:

import { Feature, useFeatureGate } from '@ops-ai/react-feature-flags-toggly'

<Feature
featureKey="PremiumCheckout"
render={(enabled) => (
<button className={enabled ? 'active' : ''} disabled={!enabled}>
Checkout
</button>
)}
/>

function CheckoutPanel() {
const { isEnabled, isLoading } = useFeatureGate(['PremiumCheckout'])
if (isLoading) return null
return <section className={isEnabled ? 'promoted' : 'muted'}>...</section>
}

Both paths subscribe to remote flag refreshes and notifyLocalGatesChanged() for device-local post-filter gates.

Users and Rollouts​

Using this package with Toggly allows you to define custom feature rollouts.

Custom rollouts offers the ability to show features only to certain groups of users based on various custom rules which you can define in Toggly.

In case you want to support custom feature rollouts, remember to provide an unique identity string for each user to make sure they get the same feature values on future visits:

const TogglyProvider = await createTogglyProvider({
appKey: 'your-app-key', // You can find this in Toggly.io
environment: 'your-environment-name', // You can find this in Toggly.io
identity: 'unique-user-identifier', // Use this in case you want to support custom feature rollouts
})

For User Claims and group targeting, call setContext() on the service after login:

import { useContext } from 'react';
import { context } from '@ops-ai/react-feature-flags-toggly';

// Inside a component rendered under the provider:
const { toggly } = useContext(context);
await toggly?.setContext({
identity: user.id,
groups: user.groups,
claims: { role: user.role, plan: user.plan },
});

See Feature filters and Evaluated-signed.

success

When using user identifiers, evaluated features are cached per user for 30 minutes by default on a sliding window, so the user might not see the change right away as to not confuse the user. The session length is configurable and the session store can be cleared in App Settings

Error reporting and refresh behavior​

Pass onError when creating the provider to observe fetch, cache, parse, and refresh failures:

const TogglyProvider = await createTogglyProvider({
appKey: 'your-app-key',
environment: 'your-environment-name',
onError: (message, error) => {
monitoring.captureException(error, {
tags: { source: 'toggly' },
extra: { message }
});
},
});

The service also exposes lastError, which is updated before onError runs. After one successful load, refresh failures keep the last-known-good flags instead of clearing rendered content. Feature, hooks, and provider state subscribe to flag refreshes, so interval and WebSocket updates can re-render React UI automatically.

For the shared reliability contract, see Reliability and Error Handling.

Basic Usage (without Toggly.io)​

You can also use the React SDK without connecting to Toggly.io by providing feature defaults:

Setup Provider with Defaults​

Import createTogglyProvider in your index file:

import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import { createTogglyProvider } from '@ops-ai/react-feature-flags-toggly'

Create a TogglyProvider and provide your feature defaults:

const featureDefaults = {
mainDescription: true,
documentationItem: true,
toolingItem: true,
}

const TogglyProvider = await createTogglyProvider({
featureDefaults: featureDefaults
})

Wrap your App component with the newly created TogglyProvider:

const root = createRoot(
document.getElementById('root') as HTMLElement,
)
root.render(
<StrictMode>
<TogglyProvider>
<App />
</TogglyProvider>
</StrictMode>,
)

Now you can use the Feature component the same way as with Toggly.io:

<Feature featureKey={'firstFeature'}>
<p>This feature can be turned on or off.</p>
</Feature>

TypeScript Support​

The React SDK includes full TypeScript support with type definitions:

import { Feature, createTogglyProvider } from '@ops-ai/react-feature-flags-toggly'

interface FeatureProps {
featureKey?: string
featureKeys?: string[]
requirement?: 'all' | 'any'
negate?: boolean
}

Best Practices​

  1. Initialize Provider Once: Create the TogglyProvider at the root of your app
  2. Use Feature Component: Prefer using the Feature component for declarative feature flag checks
  3. Provide User Context: Include identity for accurate targeting and rollouts
  4. Set Feature Defaults: Provide defaults for offline scenarios or when not using Toggly.io
  5. TypeScript: Take advantage of TypeScript support for better type safety

Extensibility with Hooks​

Toggly provides a powerful hooks system that allows you to extend SDK functionality by hooking into feature flag lifecycle events. This is perfect for integrating with analytics platforms like Microsoft Clarity, monitoring tools, or implementing custom behaviors.

What are Hooks?​

Hooks let you execute custom code at specific points in the feature flag evaluation lifecycle:

  • beforeEvaluation: Called before a feature flag is evaluated
  • afterEvaluation: Called after a feature flag is evaluated (with the result)
  • beforeIdentify: Called before user identity is set or cleared
  • afterIdentify: Called after user identity is set or cleared
  • afterRefresh: Called after feature definitions are refreshed from Toggly

Creating a Hook​

A hook is an object that implements the Hook interface from @ops-ai/toggly-hooks-types:

import { Hook } from '@ops-ai/toggly-hooks-types';

const myAnalyticsHook: Hook = {
getMetadata: () => ({
name: 'MyAnalyticsHook',
version: '1.0.0'
}),

afterEvaluation: async (data) => {
// Send to analytics
analytics.track('Feature Flag Evaluated', {
feature: data.featureKey,
enabled: data.result,
userId: data.userId
});
},

afterIdentify: async (data) => {
// Update analytics user context
analytics.identify(data.userId, data.context);
}
};

Registering Hooks​

You can register hooks in two ways:

1. During initialization:​

const TogglyProvider = await createTogglyProvider({
appKey: 'your-app-key',
environment: 'your-environment-name',
hooks: [myAnalyticsHook, myMonitoringHook]
});

To limit growth of identity-scoped localStorage cache entries, pass maxCacheKeys (see Client-side cache limits).

The package does not export useToggly. Access its service through React's useContext(context) when needed; the provider context exposes toggly. Registering hooks during provider creation avoids relying on class-only methods absent from the TogglyService interface.

Hook Execution Order​

When multiple hooks are registered:

  • before hooks execute in FIFO order (first registered, first executed)
  • after hooks execute in LIFO order (last registered, first executed)

This creates a "wrap" pattern where the first hook to start is the last to finish.

Error Isolation​

Hooks are designed to be safe:

  • If a hook throws an error, it won't affect feature flag evaluation
  • Other hooks will continue to execute
  • Errors are logged but don't propagate to your application code

Performance​

Hooks are optimized for minimal performance impact:

  • Hooks execute asynchronously without blocking evaluation
  • Hook execution is extremely fast (typically < 1ms per hook)
  • Multiple hooks can be registered without significant overhead

Common Use Cases​

Microsoft Clarity Integration​

import { Hook } from '@ops-ai/toggly-hooks-types';

const clarityHook: Hook = {
getMetadata: () => ({ name: 'Microsoft Clarity', version: '1.0.0' }),
afterEvaluation: async (data) => {
if (typeof clarity !== 'undefined') {
clarity('event', `FeatureFlag:${data.featureKey}`);
}
}
};

Debug Logging (Development Only)​

const debugHook: Hook = {
getMetadata: () => ({ name: 'DebugLogger', version: '1.0.0' }),
afterEvaluation: async (data) => {
if (process.env.NODE_ENV === 'development') {
console.debug('[Toggly]', data.featureKey, '=', data.result);
}
}
};

Google Analytics 4 Integration​

const ga4Hook: Hook = {
getMetadata: () => ({ name: 'Google Analytics 4', version: '1.0.0' }),
afterEvaluation: async (data) => {
if (typeof gtag !== 'undefined') {
gtag('event', 'feature_flag_evaluated', {
feature_name: data.featureKey,
feature_enabled: data.result,
user_id: data.userId
});
}
}
};

Entity context​

Pass a page entity per widget or check — not via global user setContext(). See Entity & page context.

Register a mapper once at startup (same as Vanilla JS):

import { registerContext } from '@ops-ai/react-feature-flags-toggly'

registerContext('Order', (order) => ({
kind: 'Order',
key: String(order.id),
attributes: { Status: order.status },
}))

Feature component​

import { Feature } from '@ops-ai/react-feature-flags-toggly'

function OrderRow({ order }) {
return (
<Feature featureKey="OrderBadge" context={order} contextKind="Order">
<span className="badge">Featured</span>
</Feature>
)
}

Programmatic checks​

import { useContext } from 'react'
import { context } from '@ops-ai/react-feature-flags-toggly'

// Inside a component under the provider; await in an effect/event handler.
const { toggly } = useContext(context)
const enabled = await toggly?.isFeatureOn('OrderBadge', order, 'Order')

Gates in evaluated-signed defs resolve offline after fetch — list rows need no extra network per item.

Next Steps​