Open Feature React SDK
The Basestack Open Feature React SDK provider allows you to use Basestack Feature Flags with the Open Feature standard in React applications. This provider bridges Basestack's feature flag infrastructure with Open Feature's standardized API, providing React hooks for seamless flag evaluation in your components.
Open Feature is an open standard for feature flag management that provides a consistent API across different providers. With this React SDK, you get React-specific hooks like useFlag, useBooleanFlagValue, and more, making it easy to use feature flags in your React components with automatic re-rendering when flags change.
Preview Status: This provider is currently in preview and still under active development. Basic feature flag functionality is supported, but advanced features like evaluation context and user identity targeting are still in the works. We recommend using this provider if you only need basic feature flag functionality.
Getting started
Install
Install the Open Feature React SDK, Web SDK, core package, and the Basestack provider. The @openfeature/react-sdk provides React hooks, while @basestack/openfeature-provider connects it to Basestack's API.
npm install @openfeature/react-sdk @openfeature/web-sdk @openfeature/core @basestack/openfeature-providerpnpm install @openfeature/react-sdk @openfeature/web-sdk @openfeature/core @basestack/openfeature-provideryarn add @openfeature/react-sdk @openfeature/web-sdk @openfeature/core @basestack/openfeature-providerbun add @openfeature/react-sdk @openfeature/web-sdk @openfeature/core @basestack/openfeature-providerEnvironment Variables
Configure your environment variables to connect to your Basestack Feature Flags project. The variable names depend on your build tool (Vite uses VITE_, Next.js uses NEXT_PUBLIC_, etc.).
When it comes to environment variables, pay attention to the framework you're using. For example, NEXT_PUBLIC_ is specific to Next.js, while in Vite.js, it would be VITE_. Example VITE_FEATURE_FLAGS_PROJECT_KEY= or FEATURE_FLAGS_PROJECT_KEY=
# BASESTACK FEATURE FLAGS
FEATURE_FLAGS_PROJECT_KEY=""
FEATURE_FLAGS_ENVIRONMENT_KEY=""You can find your project and environment keys in your Basestack Feature Flags Dashboard.
Make sure to add your .env file to .gitignore to keep your keys secure. Never commit sensitive credentials to version control.
Setup the Provider
Wrap your application with the OpenFeatureProvider component and configure the Basestack provider. The provider should be set up at the root of your application, typically in your main entry point or root layout.
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import App from "./App.tsx";
import { OpenFeatureProvider } from "@openfeature/react-sdk";
import { OpenFeature } from "@openfeature/web-sdk";
import { createBasestackWebProvider } from "@basestack/openfeature-provider/web";
const provider = createBasestackWebProvider({
apiUrl: "https://flags-api.basestack.co/v1",
projectKey: import.meta.env.VITE_FEATURE_FLAGS_PROJECT_KEY, // or process.env for other tools
environmentKey: import.meta.env.VITE_FEATURE_FLAGS_ENVIRONMENT_KEY,
prefetch: true, // Prefetch all flags on initialization
refreshIntervalMs: 30_000, // Refresh flags every 30 seconds
});
OpenFeature.setProvider(provider);
createRoot(document.getElementById("root")!).render(
<StrictMode>
<OpenFeatureProvider>
<App />
</OpenFeatureProvider>
</StrictMode>
);The provider configuration includes:
apiUrl: Your Basestack API endpointprojectKey&environmentKey: Your Basestack credentialsprefetch: Whether to fetch all flags on initializationrefreshIntervalMs: How often to refresh flags automatically (in milliseconds)
Use Flags in Components
Use React hooks to access feature flags in your components. The hooks automatically re-render components when flag values change.
import { useBooleanFlagValue } from "@openfeature/react-sdk";
function App() {
const isHeaderEnabled = useBooleanFlagValue("header", false);
return (
<div>
<h1>My App</h1>
{isHeaderEnabled && <header>New Header</header>}
</div>
);
}
export default App;The React SDK provides several hooks for different use cases:
useBooleanFlagValue(key, defaultValue): Get a boolean flag valueuseStringFlagValue(key, defaultValue): Get a string flag valueuseNumberFlagValue(key, defaultValue): Get a number flag valueuseObjectFlagValue(key, defaultValue): Get an object/JSON flag valueuseFlag(key, defaultValue): Get flag details including metadata
Basic Usage
Boolean Flags
The simplest way to use flags is with the typed value hooks:
import { useBooleanFlagValue } from "@openfeature/react-sdk";
export function Header() {
const showNewHeader = useBooleanFlagValue("new-header", false);
return showNewHeader ? <NewHeader /> : <DefaultHeader />;
}Object Flags
For complex configuration objects:
import { useObjectFlagValue } from "@openfeature/react-sdk";
export function Checkout() {
const config = useObjectFlagValue(
"checkout-config",
{ enabled: false, maxItems: 5, allowedMethods: [] }
);
if (!config.enabled) {
return <div>Checkout is disabled</div>;
}
return <CheckoutForm maxItems={config.maxItems} />;
}Flag Details
Use useFlag when you need access to flag metadata, variant, or reason:
import { useFlag } from "@openfeature/react-sdk";
export function Feature() {
const { value, variant, reason, flagMetadata } = useFlag("feature", false);
return (
<div>
<p>Feature is {value ? "enabled" : "disabled"}</p>
<p>Variant: {variant}</p>
<p>Reason: {reason}</p>
{flagMetadata && (
<pre>{JSON.stringify(flagMetadata, null, 2)}</pre>
)}
</div>
);
}Advanced Usage
Multiple Flags
Use multiple flags in a single component:
import {
useBooleanFlagValue,
} from "@openfeature/react-sdk";
export function Dashboard() {
const showHeader = useBooleanFlagValue("header", false);
const showFooter = useBooleanFlagValue("footer", false);
return (
<div>
{showHeader && <Header />}
{showFooter && <Footer />}
</div>
);
}Conditional Rendering
Use flags for conditional rendering patterns:
import { useBooleanFlagValue } from "@openfeature/react-sdk";
export function Content() {
const showNewContent = useBooleanFlagValue("new-content", false);
if (showNewContent) {
return <NewContent />;
}
return <LegacyContent />;
}Loading States
The hooks handle loading states automatically, but you can access them if needed:
import { useFlag } from "@openfeature/react-sdk";
export function LoadingFeature() {
const { value, isLoading } = useFlag("feature", false);
if (isLoading) {
return <div>Loading feature flag...</div>;
}
return value ? <Feature /> : null;
}Next.js Integration
App Router (Recommended)
For Next.js App Router, create a client-side provider component:
import "./globals.css";
import type { ReactNode } from "react";
import { Providers } from "./components/providers";
export default function RootLayout({ children }: { children: ReactNode }) {
return (
<html lang="en">
<body>
<Providers>{children}</Providers>
</body>
</html>
);
}"use client";
import type { ReactNode } from "react";
import { OpenFeatureProvider } from "@openfeature/react-sdk";
import { OpenFeature } from "@openfeature/web-sdk";
import { createBasestackWebProvider } from "@basestack/openfeature-provider/web";
export function Providers({ children }: { children: ReactNode }) {
const provider = createBasestackWebProvider({
apiUrl: "https://flags-api.basestack.co/v1",
projectKey: process.env.NEXT_PUBLIC_FEATURE_FLAGS_PROJECT_KEY,
environmentKey: process.env.NEXT_PUBLIC_FEATURE_FLAGS_ENVIRONMENT_KEY,
prefetch: true,
refreshIntervalMs: 30_000,
});
OpenFeature.setProvider(provider);
return <OpenFeatureProvider>{children}</OpenFeatureProvider>;
}Then use flags in your components:
"use client";
import { useBooleanFlagValue } from "@openfeature/react-sdk";
export default function HomePage() {
const showHeader = useBooleanFlagValue("header", false);
return (
<main>
{showHeader && <header>New Header</header>}
<h1>Welcome</h1>
</main>
);
}Custom Provider with Client Context (Advanced)
For more control, you can create a custom provider that manages the Open Feature client:
"use client";
import {
createContext,
useContext,
useEffect,
useState,
type ReactNode,
} from "react";
import { OpenFeature, type Client, type Provider } from "@openfeature/web-sdk";
import { createBasestackWebProvider } from "@basestack/openfeature-provider/web";
interface Props {
children: ReactNode;
fallback?: ReactNode;
}
const FlagsContext = createContext<Client | null>(null);
export function WebOpenFeatureProvider({ children, fallback }: Props) {
const [client, setClient] = useState<Client | null>(null);
useEffect(() => {
let mounted = true;
const provider = createBasestackWebProvider({
apiUrl: "https://flags-api.basestack.co/v1",
projectKey: process.env.NEXT_PUBLIC_FEATURE_FLAGS_PROJECT_KEY,
environmentKey: process.env.NEXT_PUBLIC_FEATURE_FLAGS_ENVIRONMENT_KEY,
refreshIntervalMs: 45_000,
});
async function init() {
try {
await OpenFeature.setProviderAndWait(provider as unknown as Provider);
if (!mounted) {
return;
}
setClient(OpenFeature.getClient("Your_Project_Name"));
} catch (error) {
console.error("Failed to initialize client-side OpenFeature", error);
}
}
init();
return () => {
mounted = false;
provider.onClose?.();
};
}, []);
if (!client) {
return <>{fallback ?? <span>Preparing client flags…</span>}</>;
}
return (
<FlagsContext.Provider value={client}>{children}</FlagsContext.Provider>
);
}
export function useFlagsClient(): Client {
const client = useContext(FlagsContext);
if (!client) {
throw new Error(
"useFlagsClient must be used within WebOpenFeatureProvider"
);
}
return client;
}Use the custom client hook:
"use client";
import { useMemo } from "react";
import { useFlagsClient } from "@/libs/openfeature-provider";
export function Header() {
const client = useFlagsClient();
const details = useMemo(
() => client.getObjectDetails("header", {}, {}),
[client]
);
return (
<header>
<pre>{JSON.stringify(details, null, 2)}</pre>
</header>
);
}Pages Router
For Next.js Pages Router, set up the provider in _app.tsx:
import type { AppProps } from "next/app";
import { OpenFeatureProvider } from "@openfeature/react-sdk";
import { OpenFeature } from "@openfeature/web-sdk";
import { createBasestackWebProvider } from "@basestack/openfeature-provider/web";
const provider = createBasestackWebProvider({
apiUrl: "https://flags-api.basestack.co/v1",
projectKey: process.env.NEXT_PUBLIC_FEATURE_FLAGS_PROJECT_KEY,
environmentKey: process.env.NEXT_PUBLIC_FEATURE_FLAGS_ENVIRONMENT_KEY,
prefetch: true,
refreshIntervalMs: 30_000,
});
OpenFeature.setProvider(provider);
export default function MyApp({ Component, pageProps }: AppProps) {
return (
<OpenFeatureProvider>
<Component {...pageProps} />
</OpenFeatureProvider>
);
}Available Hooks
The React SDK provides the following hooks:
| Hook | Description | Returns |
|---|---|---|
useBooleanFlagValue(key, defaultValue, options?) | Get a boolean flag value | boolean |
useStringFlagValue(key, defaultValue, options?) | Get a string flag value | string |
useNumberFlagValue(key, defaultValue, options?) | Get a number flag value | number |
useObjectFlagValue(key, defaultValue, options?) | Get an object flag value | T |
useFlag(key, defaultValue, options?) | Get flag details | { value, variant, reason, flagMetadata, isLoading } |
useEvaluationContext(context) | Create evaluation context | EvaluationContext |
useOpenFeatureClient() | Get the Open Feature client | Client |
Configuration Options
The createBasestackWebProvider function accepts the following configuration:
| Option | Type | Required | Default | Description |
|---|---|---|---|---|
apiUrl | string | Yes | - | Basestack API endpoint URL |
projectKey | string | Yes | - | Your Basestack project key |
environmentKey | string | Yes | - | Your Basestack environment key |
prefetch | boolean | No | false | Whether to fetch all flags on initialization |
refreshIntervalMs | number | No | 30000 | How often to refresh flags automatically (milliseconds) |
Best Practices
1. Initialize Provider at Root
Always initialize the provider at the root of your application:
// ✅ Good: Provider at root
<OpenFeatureProvider>
<App />
</OpenFeatureProvider>
// ❌ Bad: Provider nested deep
<App>
<SomeComponent>
<OpenFeatureProvider>...</OpenFeatureProvider>
</SomeComponent>
</App>2. Use Appropriate Default Values
Always provide sensible default values:
// ✅ Good: Safe default
const isEnabled = useBooleanFlagValue("risky-feature", false);
// ❌ Bad: Unsafe default
const isEnabled = useBooleanFlagValue("risky-feature", true);3. Type Your Flag Values
Use TypeScript generics for type safety:
interface Config {
enabled: boolean;
maxItems: number;
}
const config = useObjectFlagValue<Config>("config", {
enabled: false,
maxItems: 10,
});4. Handle Loading States
Consider loading states for better UX:
const { value, isLoading } = useFlag("feature", false);
if (isLoading) {
return <LoadingSpinner />;
}
return value ? <Feature /> : null;5. Use Evaluation Context for Targeting
Provide user context for targeted evaluation:
const context = useEvaluationContext({
targetingKey: userId,
email: userEmail,
});
const isEnabled = useBooleanFlagValue("feature", false, { context });6. Optimize Refresh Intervals
Balance freshness with performance:
// Frequent updates for critical flags
refreshIntervalMs: 10_000, // 10 seconds
// Less frequent for stable flags
refreshIntervalMs: 60_000, // 1 minuteTroubleshooting
Provider Not Initializing
If the provider fails to initialize:
- Check Environment Variables: Ensure your project and environment keys are set correctly
- Verify API URL: Confirm the
apiUrlis correct and accessible - Check Network: Verify your application can reach the Basestack API endpoint
- Review Console: Check browser console for detailed error messages
Flags Always Returning Default Values
If flags always return default values:
- Check Flag Keys: Verify the flag key matches what's configured in Basestack
- Verify Credentials: Ensure project and environment keys are correct
- Check Provider Status: Ensure the provider initialized successfully
- Inspect Network: Check browser network tab to see if API calls are being made
Components Not Re-rendering
If components don't re-render when flags change:
- Check Provider Setup: Ensure
OpenFeatureProviderwraps your component tree - Verify Hook Usage: Make sure you're using the hooks correctly
- Check Refresh Interval: Flags update based on
refreshIntervalMs - Review React DevTools: Check if the component is receiving new values
Type Errors
If you're getting TypeScript errors:
- Install Types: Ensure
@openfeature/coretypes are installed - Check Imports: Verify you're importing from the correct packages
- Type Assertions: Use type assertions for object values when needed
- Generic Types: Use TypeScript generics for object flag values
Performance Issues
If you're experiencing performance problems:
- Increase Refresh Interval: Reduce API calls by increasing
refreshIntervalMs - Disable Prefetch: If you don't need all flags, set
prefetch: false - Use Memoization: Memoize expensive computations based on flag values
- Optimize Re-renders: Use React.memo for components that don't need to re-render