Basestack Docs
Frontend

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.

Install the SDK
npm install @openfeature/react-sdk @openfeature/web-sdk @openfeature/core @basestack/openfeature-provider
pnpm install @openfeature/react-sdk @openfeature/web-sdk @openfeature/core @basestack/openfeature-provider
yarn add @openfeature/react-sdk @openfeature/web-sdk @openfeature/core @basestack/openfeature-provider
bun add @openfeature/react-sdk @openfeature/web-sdk @openfeature/core @basestack/openfeature-provider

Environment 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=

.env
# 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.

src/main.tsx
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 endpoint
  • projectKey & environmentKey: Your Basestack credentials
  • prefetch: Whether to fetch all flags on initialization
  • refreshIntervalMs: 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.

src/App.tsx
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 value
  • useStringFlagValue(key, defaultValue): Get a string flag value
  • useNumberFlagValue(key, defaultValue): Get a number flag value
  • useObjectFlagValue(key, defaultValue): Get an object/JSON flag value
  • useFlag(key, defaultValue): Get flag details including metadata

Basic Usage

Boolean Flags

The simplest way to use flags is with the typed value hooks:

src/components/Header.tsx
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:

src/components/Checkout.tsx
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:

src/components/Feature.tsx
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:

src/components/Dashboard.tsx
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:

src/components/Content.tsx
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:

src/components/LoadingFeature.tsx
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

For Next.js App Router, create a client-side provider component:

app/layout.tsx
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>
  );
}
components/providers.tsx
"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:

app/page.tsx
"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:

libs/openfeature-provider.tsx
"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:

components/header.tsx
"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:

pages/_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:

HookDescriptionReturns
useBooleanFlagValue(key, defaultValue, options?)Get a boolean flag valueboolean
useStringFlagValue(key, defaultValue, options?)Get a string flag valuestring
useNumberFlagValue(key, defaultValue, options?)Get a number flag valuenumber
useObjectFlagValue(key, defaultValue, options?)Get an object flag valueT
useFlag(key, defaultValue, options?)Get flag details{ value, variant, reason, flagMetadata, isLoading }
useEvaluationContext(context)Create evaluation contextEvaluationContext
useOpenFeatureClient()Get the Open Feature clientClient

Configuration Options

The createBasestackWebProvider function accepts the following configuration:

OptionTypeRequiredDefaultDescription
apiUrlstringYes-Basestack API endpoint URL
projectKeystringYes-Your Basestack project key
environmentKeystringYes-Your Basestack environment key
prefetchbooleanNofalseWhether to fetch all flags on initialization
refreshIntervalMsnumberNo30000How 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 minute

Troubleshooting

Provider Not Initializing

If the provider fails to initialize:

  1. Check Environment Variables: Ensure your project and environment keys are set correctly
  2. Verify API URL: Confirm the apiUrl is correct and accessible
  3. Check Network: Verify your application can reach the Basestack API endpoint
  4. Review Console: Check browser console for detailed error messages

Flags Always Returning Default Values

If flags always return default values:

  1. Check Flag Keys: Verify the flag key matches what's configured in Basestack
  2. Verify Credentials: Ensure project and environment keys are correct
  3. Check Provider Status: Ensure the provider initialized successfully
  4. 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:

  1. Check Provider Setup: Ensure OpenFeatureProvider wraps your component tree
  2. Verify Hook Usage: Make sure you're using the hooks correctly
  3. Check Refresh Interval: Flags update based on refreshIntervalMs
  4. Review React DevTools: Check if the component is receiving new values

Type Errors

If you're getting TypeScript errors:

  1. Install Types: Ensure @openfeature/core types are installed
  2. Check Imports: Verify you're importing from the correct packages
  3. Type Assertions: Use type assertions for object values when needed
  4. Generic Types: Use TypeScript generics for object flag values

Performance Issues

If you're experiencing performance problems:

  1. Increase Refresh Interval: Reduce API calls by increasing refreshIntervalMs
  2. Disable Prefetch: If you don't need all flags, set prefetch: false
  3. Use Memoization: Memoize expensive computations based on flag values
  4. Optimize Re-renders: Use React.memo for components that don't need to re-render