npm package: @omniretail/omniflags-react
Description: OmniFlags React SDK — provider + hooks for feature flags
Latest version: 1.0.25

# @omniretail/omniflags-react

React SDK for OmniFlags.

## Installation

```bash
npm install @omniretail/omniflags-react
```

## Quick start

Wrap your app with `OmniFlagsProvider` and use the hooks anywhere below it.

```tsx
import { OmniFlagsProvider, useFlag } from "@omniretail/omniflags-react";

function App() {
  return (
    <OmniFlagsProvider sdkKey="your-sdk-key">
      <MyApp />
    </OmniFlagsProvider>
  );
}

function MyApp() {
  const darkMode = useFlag("store.dark-mode");
  return <div className={darkMode ? "dark" : "light"}>...</div>;
}
```

## Provider

```tsx
<OmniFlagsProvider sdkKey="your-sdk-key">
  {children}
</OmniFlagsProvider>
```

| Prop | Type | Description |
|------|------|-------------|
| `sdkKey` | `string` | **Required.** SDK key from the OmniFlags dashboard. |

## Hooks

### `useFlag`

Evaluates a boolean flag.

```tsx
const enabled = useFlag("store.show-banner");
const enabled = useFlag("store.show-banner", { customerId: "cust-123" }); // with context
const enabled = useFlag("store.show-banner", undefined, false);            // custom default
```

### `useFlagValue`

Returns the typed value of a flag.

```tsx
const theme = useFlagValue<string>("store.theme", "light");
const limit = useFlagValue<number>("store.item-limit", 10);
const theme = useFlagValue<string>("store.theme", "light", { businessId: "biz-456" }); // with context
```

### `useFlagVariant`

Returns the full evaluation result — value, variant, reason, and which rule matched.

```tsx
const result = useFlagVariant("store.show-banner");
// result.value, result.variant, result.reason, result.ruleId, result.errorCode
```

### `useFlagStatus`

Returns the current loading state of the flag client.

```tsx
const { isFetching, isLoading, origin, error } = useFlagStatus();
```

| Field | Type | Description |
|-------|------|-------------|
| `isFetching` | `boolean` | A network request is in flight. |
| `isLoading` | `boolean` | No flags have been loaded yet (first fetch pending). |
| `origin` | `FlagOrigin` | Where the current flags came from: `"NONE"`, `"CACHE"`, or `"SERVER"`. |
| `error` | `Error \| null` | The last fetch error, or `null`. |

```tsx
function FlagGate({ children }: { children: React.ReactNode }) {
  const { isLoading } = useFlagStatus();
  if (isLoading) return <Spinner />;
  return <>{children}</>;
}
```

### `useOmniFlags`

Low-level access to the underlying client and ready state.

```tsx
const { client, ready } = useOmniFlags();
```

## Context

Context is passed per evaluation call — customer, business, branch, country, etc. There is no persistent context state.

```tsx
function Banner({ customerId, businessId }: Props) {
  const ctx = { customerId, businessId };
  const show = useFlag("store.show-banner", ctx);
  return show ? <Banner /> : null;
}
```

## Flag keys

Keys are namespaced by project: `{projectKey}.{flagKey}`. Find the full flag key in the OmniFlags dashboard on the flag detail page.

## Next.js (App Router)

Add the provider to your root layout. Store the SDK key in `.env.local`:

```bash
NEXT_PUBLIC_OMNIFLAGS_SDK_KEY=your-sdk-key
```

```tsx
// app/layout.tsx
import { OmniFlagsProvider } from '@omniretail/omniflags-react'

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html>
      <body>
        <OmniFlagsProvider sdkKey={process.env.NEXT_PUBLIC_OMNIFLAGS_SDK_KEY!}>
          {children}
        </OmniFlagsProvider>
      </body>
    </html>
  )
}
```

Use hooks inside `'use client'` components. Flag evaluation happens on the client after the first fetch, so guard against the initial loading state where needed:

```tsx
'use client'
import { useFlagValue, useFlagStatus } from '@omniretail/omniflags-react'

export function CheckoutButton() {
  const { isLoading } = useFlagStatus()
  const useNewFlow = useFlagValue('checkout.new-flow', false)

  if (isLoading) return null

  return <button>{useNewFlow ? 'Express checkout' : 'Checkout'}</button>
}
```

Server Components passed as `children` into the provider are unaffected — they still run on the server.