Client-side SDKs for cuped.io — A/B testing with CUPED variance reduction.
This repo is a pnpm workspace with three packages:
| Package | npm | Source | Role |
|---|---|---|---|
@cuped-io/flame |
npmjs.com | ./packages/flame | Core SDK. IIFE for <script> tag use; ESM/CJS for npm consumers. |
@cuped-io/flame-react |
npmjs.com | ./packages/flame-react | React bindings: <CupedProvider>, useExperiment, <Experiment>, useTrack. |
@cuped-io/flame-edge |
npmjs.com | ./packages/flame-edge | Edge resolver + signed cookie utilities for zero-flash SSR. Web Crypto only. |
- Fetches active experiments for a project
- Assigns visitors to variants
- Applies variant changes to the DOM (8 change types:
text,html,attribute,class,style,css,visibility,redirect) - Auto-detects e-commerce events (add to cart, checkout, etc.)
- Tracks events with experiment assignments for server-side goal matching
You'll need a DSN. Sign up at cuped.io, create a project, and copy the DSN from Settings → Install snippet (it looks like https://YOUR_KEY@api.cuped.io).
Drop this in your <head> and define variants in the dashboard. Pin to a
version and guard it with Subresource Integrity
so a bad deploy can't reach your site (crossorigin is required for SRI to
work on the cross-origin script):
<script
src="https://cdn.cuped.io/flame@0.5.0.js"
integrity="sha384-EyZs8bbbc37P0vhn5CdDiwF5Ph/dgG7+92yeULGePA4XIUw52Wx2qWQvs+hsdrLk"
crossorigin="anonymous"
data-dsn="https://YOUR_KEY@api.cuped.io"
></script>The SRI hash for every published version is listed at
cdn.cuped.io/flame.sri.json (a
versions map plus a latest pointer). Pinned URLs are write-once and never
removed, so your pin keeps resolving byte-for-byte across later releases.
Prefer auto-updates over pinning?
https://cdn.cuped.io/flame.jsis a floating "latest" that always serves the newest release. It can't be pinned with SRI (the bytes change on every release), so you trade integrity verification and rollback safety for automatic upgrades.
pnpm add @cuped-io/flame @cuped-io/flame-reactimport { CupedProvider, useExperiment } from '@cuped-io/flame-react';
function App() {
return (
<CupedProvider dsn="https://YOUR_KEY@api.cuped.io">
<Hero />
</CupedProvider>
);
}
function Hero() {
const { variant } = useExperiment('hero-cta');
return <button>{variant?.name === 'treatment' ? 'Buy now' : 'Get started'}</button>;
}The recommended setup for Next.js. Variants are resolved at the edge before the first byte renders, so server HTML matches the assigned variant from request #1.
pnpm add @cuped-io/flame @cuped-io/flame-react @cuped-io/flame-edge.env.local:
CUPED_DSN=https://YOUR_KEY@api.cuped.io
NEXT_PUBLIC_CUPED_DSN=https://YOUR_KEY@api.cuped.io
CUPED_COOKIE_SECRET=<generate with: openssl rand -base64 32>
middleware.ts:
import { createCupedMiddleware } from '@cuped-io/flame-edge/next';
export default createCupedMiddleware({
dsn: process.env.CUPED_DSN!,
secret: process.env.CUPED_COOKIE_SECRET!,
});
export const config = {
matcher: ['/((?!_next/static|_next/image|api/|favicon.ico).*)'],
};app/providers.tsx:
'use client';
import { CupedProvider } from '@cuped-io/flame-react';
import type { PrehydratedState } from '@cuped-io/flame';
export function Providers({
children,
prehydrated,
}: {
children: React.ReactNode;
prehydrated?: PrehydratedState;
}) {
return (
<CupedProvider
dsn={process.env.NEXT_PUBLIC_CUPED_DSN!}
prehydrated={prehydrated}
>
{children}
</CupedProvider>
);
}app/layout.tsx:
import { cookies } from 'next/headers';
import { readPrehydratedForServerComponent } from '@cuped-io/flame-edge/next';
import { Providers } from './providers';
export default async function RootLayout({ children }: { children: React.ReactNode }) {
const prehydrated = await readPrehydratedForServerComponent(
await cookies(),
process.env.CUPED_COOKIE_SECRET!,
);
return (
<html>
<body>
<Providers prehydrated={prehydrated ?? undefined}>{children}</Providers>
</body>
</html>
);
}That's it — useExperiment and <Experiment> work the same way as Option 2.
examples/script-tag— Static HTML page exercising all 8 change types.examples/next-app— Next.js App Router app with edge middleware + signed prehydrated cookie. Includes verification steps.
- React + Next.js guide — full reference including all hooks, the
<Experiment>component, and zero-flash SSR - Script-tag SDK
- REST API
- Methodology: CUPED & Bayesian
pnpm install
pnpm test # unit tests, all packages
pnpm typecheck
pnpm lint
pnpm build # IIFE + ESM/CJS across packages
pnpm test:e2e:install # one-time: fetch the Chromium browser
pnpm test:e2e # Playwright browser tests against both examplesThe Playwright suite (e2e/) drives the real script-tag and
Next.js examples in a browser against a mock cuped API — assignment
application, zero-flash SSR, and event delivery. See e2e/README.md.
Releases are managed by changesets and published to npm via OIDC trusted publishing with provenance attestations.
MIT — see LICENSE.