Package Exports
- @testlyjs/react
- @testlyjs/react/server
Readme
@testlyjs/react
The definitive A/B Testing infrastructure for the React ecosystem.
Testly turns A/B tests into code. Define variants directly in your frontend and let our infrastructure handle statistical distribution, user persistence, and real-time metric collection.
📖 Full Official Documentation →
🔥 Features
- ⚡ Zero Flicker: Optimized for SSR and hydration to prevent layout shifts.
- ⚛️ Universal: Compatible with React 16.8+, 17, 18, and 19 (Vite, Next.js, Remix).
- 🚀 Next.js 15 Ready: Built-in support for Server Components and hydration.
- 🛡️ Type-Safe: Native TypeScript support for variants and conversions.
- 📦 Lightweight: Under 10KB, zero external dependencies.
🚀 Quick Start
1. Installation
npm install @testlyjs/react2. Provider Configuration (Client-side)
Wrap your application at the highest level:
import { TestlyProvider } from '@testlyjs/react';
function Root() {
return (
<TestlyProvider
apiKey="your_api_key"
config={{ debug: true }} // Helpful during development
>
<App />
</TestlyProvider>
);
}3. Implementing an Experiment
Use the useExperiment hook to assign variants and track conversions.
import { useExperiment } from '@testlyjs/react';
function HeroSection() {
const { variant, convert, loading } = useExperiment('new-landing-page');
if (loading) return <div className="skeleton" />; // Handle loading state
return (
<div>
<h1>
{variant === 'variant-b'
? 'Experience the future of A/B testing'
: 'Testing made simple'}
</h1>
<button onClick={() => convert('signup-click')}>
Get Started
</button>
</div>
);
}🏗️ Next.js 15 / SSR Integration (Pro Way)
To avoid Layout Shift (CLS) and ensure zero flicker, fetch variants on the server and pass them to the provider.
1. Server-side Fetch (app/layout.tsx)
// ⚠️ Note the /server subpath!
import { getServerAssignment } from '@testlyjs/react/server';
import { cookies } from 'next/headers';
export default async function RootLayout({ children }) {
const cookieStore = await cookies();
const userId = cookieStore.get('testly_user_id')?.value || 'guest';
// Fetch assignment directly in the Server Component
const initialAssignments = await getServerAssignment({
apiKey: process.env.TESTLY_API_KEY!,
experimentKey: 'hero-cta-test',
userId
});
return (
<TestlyClientProvider initialAssignments={initialAssignments}>
{children}
</TestlyClientProvider>
);
}2. Client Provider Wrapper (components/TestlyClientProvider.tsx)
'use client';
import { TestlyProvider } from '@testlyjs/react';
export function TestlyClientProvider({ children, initialAssignments }) {
return (
<TestlyProvider
apiKey={process.env.NEXT_PUBLIC_TESTLY_KEY!}
initialAssignments={initialAssignments}
>
{children}
</TestlyProvider>
);
}🛠️ Debugging & Monitoring
Debug Mode
Enable debug: true in the configuration to see detailed logs in the browser console about assignments, impressions, and conversions.
<TestlyProvider config={{ debug: true }}>Custom Logging
Capture all SDK events (like analytics) using the onLog callback:
<TestlyProvider
config={{
onLog: (event) => {
console.log('[Testly Event]', event);
// Send to your own analytics if needed
}
}}
>Console Indicators
[Testly] Tracking impression...: SDK successfully recorded a view.[Testly] ✅ Conversion recorded...: Success![Testly] ❌ TRACKING BLOCKED: Check your API key or experiment ID.
📘 API Reference
useExperiment(key, options)
Returns the current variant for a specific experiment.
variant: The assigned variant key (or null).loading: Boolean indicating if the SDK is still initializing or fetching.convert(type, metadata): Function to track a conversion for THIS experiment.
useConversion()
A global hook to track conversions across ALL active experiments on the current page.
const convert = useConversion();
convert('global-purchase', { value: 99.90 });🛡️ Resilience
- LocalStorage Caching: Variants are cached locally to ensure a consistent user experience and zero-latency on return visits.
- Automatic Deduplication: Conversions are only tracked once per session/user (configurable).
- Graceful Fallbacks: If the API is unreachable, the SDK fails silently and uses your provided fallback values.
MIT © Testly