@trevosdk/browser
v0.2.3
Published
Lightweight browser client for Trevo experiments — deterministic variant assignment and event tracking
Downloads
3,645
Maintainers
Readme
@trevosdk/browser
Lightweight browser client for Trevo experiments — deterministic variant assignment and event tracking. Branch on getVariant() in your own code; Trevo Bot opens PRs that wire up the variants, so the SDK never mutates the DOM at runtime.
Install
npm install @trevosdk/browserCDN (script tag)
No bundler? Load the minified IIFE build from the CDN. It exposes a global
Trevo. Pin an exact version in production:
<script src="https://cdn.trevosdk.com/browser/v0.1.16/trevo.min.js"></script>
<script>
Trevo.init({ apiKey: 'tsk_live_...' });
Trevo.identify('user-42');
if (Trevo.getVariant('checkout-cta') === 'treatment') {
// show alternate experience
}
Trevo.track('checkout_started');
</script>| URL | Updates | Cache |
| --- | --- | --- |
| …/browser/v0.1.16/trevo.min.js | never (immutable) | 1 year |
| …/browser/v0/trevo.min.js | patches & minors within v0 | 5 min |
| …/browser/latest/trevo.min.js | every release | 5 min |
Use v0 or latest only for prototyping — pin a full version for production.
Quick Start
import trevo from '@trevosdk/browser';
trevo.init({ apiKey: 'tsk_live_...' });
trevo.identify('user-42');
const variant = trevo.getVariant('checkout-cta');
if (variant === 'treatment') {
// show alternate experience
}
trevo.track('checkout_started');React (client-side)
Use the @trevosdk/browser/react entry — one provider, one hook, nothing hidden:
// app root
import { TrevoProvider } from '@trevosdk/browser/react';
<TrevoProvider apiKey={process.env.NEXT_PUBLIC_TREVO_API_KEY}>{children}</TrevoProvider>;// any component
import { useExperiment } from '@trevosdk/browser/react';
const variant = useExperiment('checkout-cta');
return <p>{variant === 'treatment' ? 'A' : 'B'}</p>;Returning visitors resolve their variant from the warm cache before first paint. A first-time visitor briefly renders control until config loads, then the assigned variant — nothing is ever hidden. Exposure fires on the resolved variant. To make the first paint correct for first-time visitors too, bootstrap from the server (below). react is an optional peer dependency; apiKey should be stable for the app's lifetime.
Next.js (server bootstrap — flicker-free for everyone)
The @trevosdk/browser/next entry ships the integration, so the app-side setup is two small pieces (keep the <TrevoProvider> from the React section):
// middleware.ts — one line + the matcher; sets a stable trevo_id cookie
export { middleware } from '@trevosdk/browser/next';
export const config = {
matcher: ['/((?!_next/static|_next/image|favicon.ico|.*\\..*).*)'],
};// a server component on the experiment page — reads the cookie + env for you
import { getTrevoBootstrap } from '@trevosdk/browser/next';
const bootstrap = await getTrevoBootstrap(); // { experimentKey: variantName }
// pass to the whole provider…
<TrevoProvider apiKey={apiKey} bootstrap={bootstrap}>{children}</TrevoProvider>;
// …or to a single experiment (keeps other routes static):
const variant = useExperiment('checkout-cta', { initialVariant: bootstrap['checkout-cta'] });useExperiment then renders the correct variant on the first paint — no flicker, nothing hidden. getTrevoBootstrap reads the trevo_id cookie, so the route that calls it renders dynamically; scope it to experiment pages to keep the rest static. Requires next (an optional peer dependency). Lower-level resolveExperiments / TREVO_ID_COOKIE live in @trevosdk/browser/server for non-Next backends.
Typed variants (multi-arm)
Declare an experiment's variants once with defineExperiment to get a typed union back and exhaustive handling — so an arm added in Trevo that the code doesn't handle is a compile error, not a silent fallthrough:
import { defineExperiment, assertNever } from '@trevosdk/browser';
import { useExperiment } from '@trevosdk/browser/react';
const emailTiming = defineExperiment('email-capture-timing', [
'control',
'before-checkout',
'after-payment',
]);
function Checkout() {
const variant = useExperiment(emailTiming); // 'control' | 'before-checkout' | 'after-payment'
switch (variant) {
case 'control':
return <NoEmailCapture />;
case 'before-checkout':
return <EmailBeforeCheckout />;
case 'after-payment':
return <EmailAfterPayment />;
default:
return assertNever(variant); // fails to compile if a variant is unhandled
}
}The same defineExperiment handle works with getVariant (vanilla + server). A resolved value outside the declared set falls back to the first variant, and a mismatch between the declared set and the Trevo config logs a warning — surfacing drift between the shipped code and Trevo.
API
| Method | Description |
| --- | --- |
| init({ apiKey }) | Initialize the SDK. Starts event queue and config polling. |
| identify(userId) | Set the authenticated user. May change variant assignment. |
| getVariant(experimentKey, options?) | Returns the assigned variant ("control" as fallback). Fires an exposure event by default; pass { trackExposure: false } to read the assignment for rendering without exposure. |
| trackExposure(experimentKey, variantName) | Emit an exposure explicitly — call when the treatment surface is actually visible. Deduplicated per identity; a no-op unless the experiment/variant is configured. |
| isReady() | true once config has loaded (or the first fetch failed). Use to read getVariant() synchronously during render. |
| ready() | Resolves once config has loaded (or the first fetch failed). |
| track(eventName, properties?) | Enqueue a tracking event. Batched and flushed automatically. |
| flush() | Manually flush all queued events. |
Documentation
Full docs at docs.trevosdk.com
