Documentation Index
Fetch the complete documentation index at: https://omniflagsdoc.omniretail.app/llms.txt
Use this file to discover all available pages before exploring further.
Installation
npm install @omniretail/omniflags-react
Peer dependencies: React 18+
Setup
Wrap your app root with OmniFlagsProvider. It fetches the flag snapshot and starts background polling.
Vite / CRA
Next.js (App Router)
Next.js (Pages Router)
// main.tsx
import { createRoot } from 'react-dom/client';
import { OmniFlagsProvider } from '@omniretail/omniflags-react';
import App from './App';
createRoot(document.getElementById('root')!).render(
<OmniFlagsProvider sdkKey="pk_live_...">
<App />
</OmniFlagsProvider>
);
Import OmniFlagsProvider from the /nextjs entry point and use it
directly in your root layout.// app/layout.tsx
import { OmniFlagsProvider } from '@omniretail/omniflags-react/nextjs';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<OmniFlagsProvider sdkKey={process.env.NEXT_PUBLIC_OMNIFLAGS_SDK_KEY!}>
{children}
</OmniFlagsProvider>
</body>
</html>
);
}
Any component that uses flag hooks must be a Client Component:// app/components/promo-banner.tsx
'use client';
import { useFlagValue } from '@omniretail/omniflags-react';
export function PromoBanner() {
const colour = useFlagValue('product-listing.promo-banner-colour', 'blue');
return <div style={{ background: colour }}>Limited time offer</div>;
}
// app/page.tsx
import { PromoBanner } from './components/promo-banner';
export default function Page() {
return (
<main>
<PromoBanner />
</main>
);
}
// pages/_app.tsx
import type { AppProps } from 'next/app';
import { OmniFlagsProvider } from '@omniretail/omniflags-react';
export default function MyApp({ Component, pageProps }: AppProps) {
return (
<OmniFlagsProvider sdkKey={process.env.NEXT_PUBLIC_OMNIFLAGS_SDK_KEY!}>
<Component {...pageProps} />
</OmniFlagsProvider>
);
}
Loading state
Hooks return defaultValue until the first snapshot arrives. If you need to gate rendering on that:
import { useFlagStatus } from '@omniretail/omniflags-react';
function AppShell() {
const { isLoading } = useFlagStatus();
if (isLoading) return <FullScreenSpinner />;
return <App />;
}
Most apps don’t need this. Hooks update in place once the snapshot arrives.
When using OmniFlagsProvider (Next.js App Router), flags are ready before
the first render. isLoading will always be false and defaultValue fallbacks
are never shown.
Hooks
useFlag
Boolean flag evaluation. Re-renders only when this flag’s value changes.
import { useFlag } from '@omniretail/omniflags-react';
function PromotionalBanner({ customerId }: { customerId: string }) {
const showBanner = useFlag('product-listing.show-promo-products', { customerId });
if (!showBanner) return null;
return <Banner />;
}
| Parameter | Type | Default | Description |
|---|
flagKey | string | required | {projectKey}.{flagKey} |
context | EvaluationContext | {} | Targeting attributes |
defaultValue | boolean | false | Fallback when flag is missing, wrong type, or client not ready |
useFlagValue
Returns the typed value of any flag: string, number, or object.
import { useFlagValue } from '@omniretail/omniflags-react';
function PromoBanner({ customerId }: { customerId: string }) {
const colour = useFlagValue('product-listing.promo-banner-colour', 'blue', { customerId });
return <div style={{ backgroundColor: colour }}>Limited time offer</div>;
}
const maxCartItems = useFlagValue('cart.max-items', 20, { customerId });
const config = useFlagValue<CheckoutConfig>('checkout.config', defaultConfig, { customerId });
| Parameter | Type | Required | Description |
|---|
flagKey | string | yes | {projectKey}.{flagKey} |
defaultValue | T | yes | Fallback value |
context | EvaluationContext | no | Targeting attributes |
useFlagVariant
Returns the full EvaluationResult (variant key, reason, and rule ID). Useful when you need to log evaluation details or branch on variant names directly.
import { useFlagVariant } from '@omniretail/omniflags-react';
function BannerWithAnalytics({ customerId }: { customerId: string }) {
const result = useFlagVariant('product-listing.promo-banner-colour', { customerId });
useEffect(() => {
analytics.track('flag_evaluated', {
flag: 'product-listing.promo-banner-colour',
variant: result.variant,
reason: result.reason,
ruleId: result.ruleId,
});
}, [result.variant, result.reason]);
return <Banner colour={result.value as string} />;
}
| Field | Type | Description |
|---|
value | unknown | Resolved flag value |
variant | string | null | Matched variant key |
reason | EvaluationReason | See reason codes |
ruleId | string | null | Matched rule ID, if any |
errorCode | ErrorCode | null | Set when reason is ERROR |
useFlagStatus
Current state of the flag client.
import { useFlagStatus } from '@omniretail/omniflags-react';
function DiagnosticBadge() {
const { isLoading, isFetching, origin, error } = useFlagStatus();
if (error) console.error('OmniFlags fetch failed:', error);
return <span title={`Flags: ${origin}`}>{isFetching ? '⟳' : '✓'}</span>;
}
| Field | Type | Description |
|---|
isLoading | boolean | true until the first snapshot loads (cache or network) |
isFetching | boolean | true while a network request is in flight |
origin | 'NONE' | 'CACHE' | 'SERVER' | Source of the active snapshot |
error | Error | null | Last fetch error |
useSetFlagContext
Sets a global evaluation context for the whole provider tree. Individual hook calls that pass their own context will be merged with it; per-call attributes win on conflict.
import { useSetFlagContext } from '@omniretail/omniflags-react';
function AuthenticatedApp({ user }: { user: CurrentUser }) {
useSetFlagContext({
customerId: user.id,
country: user.country,
platform: 'web',
});
return <App />;
}
Clears on unmount.
| Parameter | Type | Description |
|---|
context | EvaluationContext | undefined | Pass undefined to clear explicitly. |
Evaluation context
Attributes used to match targeting rules and assign rollout buckets. Pass per-hook or once globally via useSetFlagContext.
useFlag('checkout.charge-delivery-fee', {
customerId: '4821',
country: 'Nigeria',
city: 'Lagos',
platform: 'web',
appVersion: '4.1.0',
});
| Attribute | Type | Notes |
|---|
customerId | string | number | Primary bucketing key |
agentId | string | number | Used when customerId is absent |
businessId | string | number | |
businessBranchId | string | number | |
country | string | e.g. "Nigeria", "Ghana" |
city | string | |
platform | string | web, ios, android |
appVersion | string | Semver string |
Any extra key-value pairs are forwarded to targeting rules as custom attributes.
Rollout and traffic splits require a bucketing key (customerId, agentId, etc.). Without one the SDK returns the flag’s default value with MISSING_TARGETING_KEY.