Basestack Docs
Frontend

React (Official SDK)

Seamless integration with React streamlines the testing and development of features across production and various environments. The Basestack React SDK provides React hooks and components that make it easy to use feature flags in any React application, whether you're using Vite, Create React App, or a custom React setup.

The SDK leverages React Context to provide feature flags throughout your component tree, eliminating the need to pass props manually. It includes built-in caching, loading states, and error handling to ensure a smooth developer experience.

Looking for Next.js integration? Check out the Next.js SDK instead.

Getting started

We only support ESM (ECMAScript Modules) in the browser or server environment. If you are using CommonJS, you will need to use a bundler like Webpack or Rollup to bundle your code.

Examples

Install

First, let's install the required packages! The React SDK requires @basestack/flags-react for React-specific functionality and @basestack/flags-js as the core SDK dependency.

Install the SDK
npm install @basestack/flags-react @basestack/flags-js
pnpm install @basestack/flags-react @basestack/flags-js
yarn add @basestack/flags-react @basestack/flags-js
bun add @basestack/flags-react @basestack/flags-js

Environment Variables

Configure your environment variables to connect to your Basestack Feature Flags project. The prefix depends on your build tool:

  • Vite: Use VITE_ prefix
  • Create React App: Use REACT_APP_ prefix
  • Custom setup: Use any prefix your bundler supports

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_BASE_URL= or Node.js FEATURE_FLAGS_BASE_URL=

.env
# BASESTACK FEATURE FLAGS
VITE_FEATURE_FLAGS_BASE_URL="https://flags.basestack.co/api/v1"
VITE_FEATURE_FLAGS_PROJECT_KEY=""
VITE_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.

Create Configuration File

Create a configuration file to centralize your SDK settings. This makes it easy to reuse the configuration throughout your application and update it in one place.

src/flagsConfig.ts
import type { SDKConfig } from "@basestack/flags-js";

// For Vite projects
export const flagsConfig: SDKConfig = {
  baseURL: import.meta.env.VITE_FEATURE_FLAGS_BASE_URL,
  projectKey: import.meta.env.VITE_FEATURE_FLAGS_PROJECT_KEY,
  environmentKey: import.meta.env.VITE_FEATURE_FLAGS_ENVIRONMENT_KEY,
};

// For Create React App, use this instead:
// export const flagsConfig: SDKConfig = {
//   baseURL: process.env.REACT_APP_FEATURE_FLAGS_BASE_URL,
//   projectKey: process.env.REACT_APP_FEATURE_FLAGS_PROJECT_KEY,
//   environmentKey: process.env.REACT_APP_FEATURE_FLAGS_ENVIRONMENT_KEY,
// };

Setup the Provider

The FlagsProvider is a React Context Provider that wraps your application and provides feature flags to all child components. It manages the SDK client instance, caches flags, and handles loading states.

In your application entry point (typically main.tsx or index.tsx), wrap your app with the FlagsProvider. For better performance, you can optionally preload flags before rendering your app.

src/main.tsx
import type { Flag } from "@basestack/flags-js";
import { FlagsProvider } from "@basestack/flags-react/client";
import { fetchFlags } from "@basestack/flags-react/server";
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { App } from "./App";
import { flagsConfig } from "./flagsConfig";

async function bootstrap() {
  const container = document.getElementById("root");

  if (!container) {
    throw new Error("Root container #root was not found.");
  }

  // Optionally preload flags before rendering
  // This reduces loading states and improves initial render performance
  let initialFlags: Flag[] = [];

  try {
    initialFlags = await fetchFlags(flagsConfig);
  } catch (error) {
    console.error("Failed to preload flags", error);
    // App will still work, flags will be fetched on-demand
  }

  const root = createRoot(container);

  root.render(
    <StrictMode>
      <FlagsProvider
        config={flagsConfig}
        initialFlags={initialFlags}
        preload={initialFlags.length === 0}
      >
        <App />
      </FlagsProvider>
    </StrictMode>,
  );
}

void bootstrap();

Key points:

  • initialFlags: Preloaded flags from server-side fetch (optional but recommended)
  • preload: Set to true if no initial flags are provided, so flags are fetched automatically
  • config: Your SDK configuration object

Use Flags in Components

Now you can use the useFlag hook in any component within your provider tree. The hook returns the flag's enabled state, payload, loading status, and a refresh function.

src/App.tsx
import { useFlag } from "@basestack/flags-react/client";

export function App() {
  const { enabled, payload, isLoading, refresh } = useFlag<{
    color?: string;
  }>("header");

  if (isLoading) {
    return <div>Loading feature flags...</div>;
  }

  return (
    <section style={{ fontFamily: "sans-serif", padding: "2rem" }}>
      <h1>React + Vite Example</h1>
      <p>
        Flag <code>header</code> is{" "}
        {enabled ? "enabled" : "disabled"}
      </p>
      {enabled && payload ? (
        <p>Payload: {JSON.stringify(payload, null, 2)}</p>
      ) : null}
      <button type="button" onClick={() => refresh()}>
        Refresh flag
      </button>
    </section>
  );
}

The useFlag hook:

  • Automatically fetches the flag if it's not in cache
  • Returns loading state while fetching
  • Provides a refresh function to manually refetch the flag
  • Supports TypeScript generics for type-safe payloads

Advanced Usage

Using Multiple Flags

You can use multiple useFlag hooks in the same component, or use useFlags to get all flags at once:

src/components/FeatureShowcase.tsx
import { useFlag, useFlags } from "@basestack/flags-react/client";

export function FeatureShowcase() {
  const headerFlag = useFlag<{ variant: string }>("header");
  const footerFlag = useFlag("footer");
  const { flags, isLoading } = useFlags();

  return (
    <div>
      <h2>Individual Flags</h2>
      <p>Header: {headerFlag.enabled ? "Enabled" : "Disabled"}</p>
      <p>Footer: {footerFlag.enabled ? "Enabled" : "Disabled"}</p>

      <h2>All Flags</h2>
      {isLoading ? (
        <p>Loading...</p>
      ) : (
        <ul>
          {flags.map((flag) => (
            <li key={flag.slug}>
              {flag.slug}: {flag.enabled ? "enabled" : "disabled"}
            </li>
          ))}
        </ul>
      )}
    </div>
  );
}

Conditional Rendering

Use flags to conditionally render features:

src/components/ConditionalFeature.tsx
import { useFlag } from "@basestack/flags-react/client";

export function ConditionalFeature() {
  const { enabled, payload } = useFlag<{ theme: string }>("new-ui");

  if (!enabled) {
    return <OldUI />;
  }

  return <NewUI theme={payload?.theme} />;
}

Accessing the SDK Client Directly

For advanced use cases, you can access the SDK client directly using useFlagsClient:

src/components/AdvancedUsage.tsx
import { useFlagsClient } from "@basestack/flags-react/client";
import { useEffect, useState } from "react";
import type { Flag } from "@basestack/flags-js";

export function AdvancedUsage() {
  const client = useFlagsClient();
  const [flag, setFlag] = useState<Flag | null>(null);

  useEffect(() => {
    async function fetchFlag() {
      const result = await client.getFlag("header");
      setFlag(result);
    }
    fetchFlag();
  }, [client]);

  return flag ? <div>Flag: {flag.slug}</div> : <div>Loading...</div>;
}

Error Handling

Always handle potential errors when working with flags:

src/components/ErrorHandling.tsx
import { useFlag } from "@basestack/flags-react/client";

export function ErrorHandling() {
  const { enabled, error, isLoading } = useFlag("header");

  if (error) {
    return (
      <div>
        <p>Failed to load feature flag: {error.message}</p>
        <p>Falling back to default behavior...</p>
        {/* Render default UI */}
      </div>
    );
  }

  if (isLoading) {
    return <div>Loading...</div>;
  }

  return <div>Feature is {enabled ? "enabled" : "disabled"}</div>;
}

Best Practices

1. Preload Flags on App Start

Preloading flags before rendering your app eliminates loading states and improves user experience:

// In main.tsx
const initialFlags = await fetchFlags(flagsConfig);
// Pass to FlagsProvider

2. Use TypeScript Generics for Payloads

Type your flag payloads for better type safety:

const { payload } = useFlag<{ variant: string; color: string }>("header");
// payload is now typed as { variant: string; color: string } | undefined

3. Handle Loading States

Always handle loading states for better UX:

const { enabled, isLoading } = useFlag("header");

if (isLoading) return <LoadingSpinner />;

4. Provide Fallback Values

Use default values when flags fail to load:

const { enabled, error } = useFlag("header");
const isEnabled = error ? false : enabled; // Fallback to false on error

5. Cache Considerations

The SDK includes built-in caching (5 minutes default). Flags are automatically cached after first fetch, reducing unnecessary API calls. You can configure cache settings in the SDK config:

const flagsConfig: SDKConfig = {
  // ... other config
  cache: {
    enabled: true,
    ttl: 10 * 60 * 1000, // 10 minutes
    maxSize: 50, // Max 50 cached flags
  },
};

6. Avoid Unnecessary Re-renders

The hooks are optimized to prevent unnecessary re-renders. However, if you're using flags in many components, consider using useMemo for expensive computations:

const { enabled } = useFlag("header");
const expensiveValue = useMemo(() => {
  // Expensive computation based on enabled
  return computeSomething(enabled);
}, [enabled]);

Troubleshooting

Flags Not Loading

If flags aren't loading:

  1. Check Environment Variables: Ensure your environment variables are correctly prefixed (VITE_ for Vite, REACT_APP_ for CRA)
  2. Verify Provider Setup: Make sure FlagsProvider wraps your entire app
  3. Check Network: Verify your application can reach the Basestack API endpoint
  4. Check Console: Look for error messages in the browser console

Components Rendering Twice

If your components are rendering twice, this is likely due to React.StrictMode in development. This is expected behavior and helps identify potential issues. In production, components will only render once.

TypeScript Errors

If you're getting TypeScript errors:

  1. Install Types: Ensure @basestack/flags-js and @basestack/flags-react are installed
  2. Check Imports: Use the correct import paths (@basestack/flags-react/client for hooks)
  3. Type Payloads: Use TypeScript generics to type your payloads

Environment Variables Not Available

If environment variables aren't available:

  1. Check Prefix: Use the correct prefix for your build tool
  2. Restart Dev Server: After adding environment variables, restart your dev server
  3. Verify .env File: Ensure your .env file is in the project root
  4. Check Build Tool Config: Some build tools require additional configuration

Flags Not Updating

If flags aren't updating:

  1. Check Cache: Flags are cached for 5 minutes by default. Wait for cache to expire or clear it programmatically
  2. Use Refresh: Call the refresh() function returned by useFlag to manually refetch
  3. Check Flag State: Verify the flag state in your Basestack dashboard

API Reference - Props & Parameters

Components

FlagsProvider

PropTypeRequiredDefaultDescription
configSDKConfigYes-Configuration object for the Flags SDK
childrenReactNodeYes-React children to be wrapped by the provider
initialFlagsFlag[]No[]Initial flags to populate the provider with
preloadbooleanNotrueWhether to automatically preload flags on mount
onError(error: Error) => voidNo-Callback function called when an error occurs

Hooks

useFlag<TPayload>(slug, options?)

Parameters:

ParameterTypeRequiredDefaultDescription
slugstringYes-The flag slug to retrieve
optionsUseFlagOptions<TPayload>No-Options object (see below)

Options (UseFlagOptions):

OptionTypeRequiredDefaultDescription
defaultEnabledbooleanNofalseDefault enabled state if flag is not loaded
defaultPayloadTPayloadNo-Default payload value if flag is not loaded
fetchbooleanNotrueWhether to automatically fetch the flag if not cached

Returns (UseFlagResult<TPayload>):

PropertyTypeDescription
flagFlag | undefinedThe flag object if available
enabledbooleanWhether the flag is enabled
payloadTPayload | undefinedThe flag payload
isLoadingbooleanWhether the flag is currently loading
errorError | nullAny error that occurred during fetching
refresh() => Promise<Flag | undefined>Function to manually refresh the flag

useFlags()

Parameters: None

Returns:

PropertyTypeDescription
flagsFlag[]Array of all flags
flagsBySlugRecord<string, Flag>Object mapping flag slugs to flag objects
isLoadingbooleanWhether flags are currently loading
errorError | nullAny error that occurred during fetching
refresh() => Promise<void>Function to manually refresh all flags

useFlagsClient()

Parameters: None

Returns:

PropertyTypeDescription
(client)FlagsSDKThe Flags SDK client instance

useFlagsContext()

Parameters: None

Returns:

PropertyTypeDescription
clientFlagsSDKThe Flags SDK client instance
flagsMap<string, Flag>Map of flags by slug
loadingbooleanWhether flags are currently loading
errorError | nullAny error that occurred
refresh() => Promise<void>Function to manually refresh all flags
upsertFlag(flag: Flag) => voidFunction to update or insert a flag

Server Functions

fetchFlags(config)

ParameterTypeRequiredDescription
configSDKConfigYesConfiguration object for the Flags SDK

Returns: Promise<Flag[]> - Array of fetched flags

Note: This function can be used in any JavaScript environment (browser or Node.js) to fetch flags before rendering your React app.

Types

SDKConfig

Exported from @basestack/flags-js - Configuration for the Flags SDK

Flag

Exported from @basestack/flags-js - Flag object type

CacheConfig

Exported from @basestack/flags-js - Cache configuration type