> ## Documentation Index
> Fetch the complete documentation index at: https://docs.trevosdk.com/llms.txt
> Use this file to discover all available pages before exploring further.

# React

> One provider and one hook.

`@trevosdk/react` wraps the browser SDK in a provider and a hook. It depends on
[`@trevosdk/browser`](/install/browser) under the hood, so one install is all you need:

```bash theme={null}
npm install @trevosdk/react
```

`react` >= 18 is a peer dependency.

## Provider

Wrap your app once, as high as you can:

```tsx theme={null}
import { TrevoProvider } from '@trevosdk/react';

const apiKey = import.meta.env.VITE_TREVO_API_KEY;

<TrevoProvider apiKey={apiKey}>
  {children}
</TrevoProvider>
```

The example uses Vite. Match the public configuration mechanism the app already uses:
`REACT_APP_TREVO_API_KEY` for Create React App, `GATSBY_TREVO_API_KEY` for Gatsby,
or the app's existing browser-safe runtime config for another bundler. Do not copy a
Next.js `NEXT_PUBLIC_` variable into a non-Next app.

The provider initialises the SDK synchronously on first render, keyed on `apiKey`. A key
that arrives later — from runtime config, a consent gate or env hydration — initialises the
SDK on the render it appears, and changing the key re-initialises against the new workspace.

The provider emits one `page_view` event (with `path`) when it initialises — don't add your
own `page_view` call or visits count twice.

The provider also accepts `configUrl` and `ingestionUrl` props (endpoint overrides for
proxied installs).

Passing `apiKey={undefined}` keeps the SDK from ever starting — a clean way to keep it off
outside production. It doesn't stop an SDK that already initialised this session; for
consent withdrawal call `trevo.optOut()`.

## Hook

```tsx theme={null}
import { useExperiment } from '@trevosdk/react';

function CheckoutButton() {
  const variant = useExperiment('checkout-cta');

  return variant === 'treatment' ? <NewCta /> : <CurrentCta />;
}
```

Exposure is recorded on the variant that actually renders. The hook stays subscribed to the
SDK, so a variant that could not be resolved on first paint — the first config fetch failed,
or `identify()` had not been called yet — updates as soon as it can be.

### Typed variants

Pass an `Experiment` from `defineExperiment()` (re-exported here) and the return type
narrows to a union, so an unhandled arm is a compile error:

```tsx theme={null}
import { assertNever, defineExperiment, useExperiment } from '@trevosdk/react';

const emailTiming = defineExperiment('email-capture-timing', [
  'control',
  'before-checkout',
  'after-payment',
]);

function Checkout() {
  const variant = useExperiment(emailTiming);

  switch (variant) {
    case 'control':         return <NoEmailCapture />;
    case 'before-checkout': return <EmailBeforeCheckout />;
    case 'after-payment':   return <EmailAfterPayment />;
    default:                return assertNever(variant);
  }
}
```

If the variants you declare here drift from the ones configured in Trevo, the SDK logs a
warning — which surfaces the mismatch instead of silently returning control.

Keep `'control'` in the list. A paused, ended or unknown experiment returns `'control'`; if
you did not declare it, the hook renders your first declared variant instead and logs a
warning telling you to update `defineExperiment()`, even though nothing drifted.

## What renders on the first paint

This is the part worth understanding before you ship an experiment above the fold.

* **Returning visitors** resolve from a warm local-storage cache and render the correct
  variant immediately.
* **First-time visitors** have no cache. They render `control` for one paint, then swap to
  their assigned variant when config arrives.

Nothing is ever hidden, and exposure fires on the variant that actually rendered — so the
data stays correct either way. But a visible swap on a first visit can look like a glitch.

Two ways to avoid it:

1. **Resolve on the server.** See [Next.js](/install/nextjs) — the first paint is already
   correct for everyone, with nothing hidden. Pass the server-resolved assignments as
   `bootstrap` on the provider, or per experiment as
   `useExperiment(key, { initialVariant })`.
2. **Use the anti-flicker snippet.** See [Browser](/install/browser#anti-flicker). Hides the
   page briefly rather than showing the wrong thing.

If neither applies, prefer experiments below the fold or behind an interaction, where a
first-paint swap is not visible.

## Accessing the client directly

```tsx theme={null}
import { useTrevo } from '@trevosdk/react';

const trevo = useTrevo();      // the SDK instance, or null before init
trevo?.track('purchase_completed', { value: 49.99 });
```

Use this for `track()` and `identify()`. For reading variants, prefer `useExperiment()` —
it handles re-rendering when config arrives.

## Identifying users

Call `identify()` as soon as you know who the user is, typically in an effect after auth
resolves:

```tsx theme={null}
const trevo = useTrevo();

useEffect(() => {
  if (user) trevo?.identify(user.id);
}, [user, trevo]);
```

Because identity determines assignment, calling `identify()` after an experiment has
rendered can move the user to a different variant mid-session. Identify before rendering
anything under test.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.