Skip to main content
Create project
@sheepit-ai/react

React SDK

React bindings for Sheepit. Provides a context provider, hooks, and declarative components.

GoaTechProvider

The root provider that initializes the SDK and makes the client available to all hooks and components in the tree. Mount it once, near the top of your component hierarchy — typically alongside your other context providers.

Props

PropTypeDescription
configGoaTechConfigSDK configuration object. Requires at minimum an apiKey.
user{ id: string; traits?: Record<string, unknown> } | nullCurrent user. When this prop changes the SDK automatically calls identify() for a logged-in user or reset() when set to null. Optional.
childrenReactNodeYour application subtree.

The example below wires the provider to your existing auth layer. As soon as user changes — after login, logout, or session refresh — the SDK updates identity automatically.

app/providers.tsx
"use client";

import { GoaTechProvider } from "@sheepit-ai/react";
import { useAuth } from "@/hooks/use-auth";

export function AppProviders({ children }: { children: React.ReactNode }) {
  const { user } = useAuth();

  return (
    <GoaTechProvider
      config={{
        apiKey: process.env.NEXT_PUBLIC_GT_KEY!,
        environment: "production",
      }}
      user={user ? { id: user.id, traits: { email: user.email, plan: user.plan } } : null}
    >
      {children}
    </GoaTechProvider>
  );
}

Hooks

All hooks must be used inside a GoaTechProvider subtree. They are safe to call unconditionally — no conditional hook calls needed for loading states.

useGoaTech()

Returns the raw IGoaTech client instance. Use this for advanced cases where the higher-level hooks do not expose the API you need — for example inspecting the event queue or manually calling flush().

tsx
const gt = useGoaTech();
const status = gt.status();
console.log("Queue depth:", status.queueDepth);

useFlag(flagKey, defaultValue?)

Evaluates a feature flag and returns its current value. The return type matches the flag's configured type — boolean, string, number, or object. The hook automatically tracks a flag exposure event the first time the flag is evaluated in a session, so your analytics stay accurate without any extra calls.

defaultValue is returned when the SDK has not yet fetched flag configuration or when the flag does not exist.

tsx
const showBanner = useFlag("maintenance_banner", false);
const theme = useFlag("app_theme", "light");
const limit = useFlag("upload_limit_mb", 10);

useExperiment(experimentKey)

Returns the current user's experiment assignment as { variant: string; payload?: Record<string, unknown> }. The payload field carries any structured data attached to that variant in the Sheepit dashboard — useful for passing copy, colours, or configuration without a code deploy. If the user is not assigned to the experiment, variant is "control". Exposure is tracked automatically on first evaluation.

tsx
const { variant, payload } = useExperiment("onboarding_flow");

useTrack()

Returns a stable track(eventName, properties?) function. The function reference is stable across re-renders, making it safe to pass as a prop or include in useEffect and useCallback dependency arrays without triggering unnecessary re-renders.

tsx
const track = useTrack();

function handleClick() {
  track("button_clicked", { buttonId: "cta-hero" });
}

Components

Declarative wrappers around the core hooks. They keep flag and experiment logic out of your render functions and make feature branching readable at a glance.

<Feature>

Conditionally renders children based on a feature flag. When the flag is falsy (or does not match the optional match value), fallback is rendered instead — or nothing if fallback is omitted.

Props

PropTypeDescription
flagKeystringThe flag to evaluate.
childrenReactNodeRendered when the flag is truthy (or matches).
fallbackReactNodeRendered when the flag is falsy. Optional.
matchFlagValueWhen provided, children are only rendered when the flag value strictly equals this value. Useful for string or number flags. Optional.
tsx
// Boolean flag
<Feature flagKey="new_dashboard">
  <NewDashboard />
</Feature>

// With fallback
<Feature flagKey="beta_feature" fallback={<ComingSoon />}>
  <BetaFeature />
</Feature>

// Match specific value
<Feature flagKey="pricing_tier" match="enterprise">
  <EnterpriseFeatures />
</Feature>

<Experiment>

Renders different content based on the current user's experiment variant. Two usage patterns are supported: a variant map (declarative object of variant → ReactNode) and a render prop (function receiving the full experiment result). Use the variant map for simple cases and the render prop when you need access to the variant payload.

Props

PropTypeDescription
experimentKeystringThe experiment to evaluate.
variantsRecord<string, ReactNode>Map of variant names to nodes. Used in Pattern 1. Optional.
children(result: ExperimentResult) => ReactNodeRender prop receiving the full result. Used in Pattern 2. Optional.
fallbackReactNodeRendered when no matching variant is found in the map. Optional.
tsx
// Pattern 1: Variant map
<Experiment
  experimentKey="hero_test"
  variants={{
    control: <HeroA />,
    variant_b: <HeroB />,
    variant_c: <HeroC />,
  }}
  fallback={<HeroA />}
/>

// Pattern 2: Render prop
<Experiment experimentKey="hero_test">
  {({ variant, payload }) => {
    switch (variant) {
      case "variant_b": return <HeroB config={payload} />;
      default: return <HeroA />;
    }
  }}
</Experiment>

<PageViewTracker>

Automatically tracks a page_view event whenever pathname or searchParams changes. Place it once in your root layout inside a <Suspense> boundary — required because it calls useSearchParams() internally.

Props

PropTypeDescription
pathnamestringCurrent route pathname.
searchParamsstringCurrent query string (e.g. from searchParams.toString()). Optional.
components/analytics.tsx
"use client";
import { usePathname, useSearchParams } from "next/navigation";
import { PageViewTracker } from "@sheepit-ai/react";

export function Analytics() {
  const pathname = usePathname();
  const searchParams = useSearchParams();

  return (
    <PageViewTracker
      pathname={pathname}
      searchParams={searchParams.toString()}
    />
  );
}

SSR Safety

The provider gracefully handles server-side rendering. When rendered on the server — or when no apiKey is provided — it falls back to a noop client that returns safe defaults for every call:

  • useFlag(key, default) returns default.
  • useExperiment(key) returns { variant: "control" }.
  • track() calls are silently ignored — no errors thrown.

This means you can write your components once and they will behave correctly during SSR, static generation, and hydration without any special guard code.