Signal — @sonordev/site-kit/signal
Real-time A/B experiments, behavior tracking, and dynamic configuration from Signal AI. Requires full_signal plan.
Usage
Auto-included by SiteKitLayout when signal prop is enabled:
<SiteKitLayout signal>{children}</SiteKitLayout>For standalone use:
'use client'
import { SignalBridge } from '@sonordev/site-kit/signal'
export default function Providers({ children }) {
return <SignalBridge>{children}</SignalBridge>
}A/B Experiments
Declarative
import { SignalExperiment } from '@sonordev/site-kit/signal'
<SignalExperiment
experimentId="hero-cta"
variants={{
control: <Button>Get Started</Button>,
variant_a: <Button>Start Free Trial</Button>,
}}
trackImpression
fallback={<Button>Default</Button>}
/>Hook-Based
import { useSignalExperiment } from '@sonordev/site-kit/signal'
function HeroCTA() {
const { variant, isControl } = useSignalExperiment('hero-cta')
return isControl ? <Button>Get Started</Button> : <Button>Start Free Trial</Button>
}Conversion Tracking
import { ExperimentConversion } from '@sonordev/site-kit/signal'
<ExperimentConversion experimentId="hero-cta" conversionType="click">
<Button>Sign Up</Button>
</ExperimentConversion>Hooks
useSignal() // Full context: config, loading, trackEvent, trackOutcome
useSignalConfig() // Just the config object
useSignalEvent() // Returns trackEvent function
useSignalOutcome() // Returns trackOutcome function
useSignalExperiment(id) // Returns { assignment, variant, isControl }SignalBridge Props
interface SignalBridgeProps {
enabled?: boolean // Default: true
realtime?: boolean // SSE real-time updates (default: true)
experiments?: boolean // Participate in A/B tests (default: true)
behaviorTracking?: boolean // Scroll, clicks, time-on-page (default: true)
children: React.ReactNode
}What It Does
- Fetches config from
GET /api/public/signal/config - Opens SSE stream for real-time
config_updateandexperiment_updateevents - Assigns experiment variants per visitor (cached)
- Batches behavioral events (scroll depth, click count, time-on-page) and flushes on debounce
- Tracks outcomes/conversions via POST
Key Types
interface ExperimentConfig {
id: string; name: string;
status: 'draft' | 'running' | 'paused' | 'completed';
variants: ExperimentVariant[];
traffic_allocation: number; // 0-1
goal: string;
winner?: string;
}
interface ExperimentVariant {
key: string; name: string; weight: number; description?: string;
}
interface SignalEvent {
event_type: string; event_name: string; event_data?: object;
page_url: string; page_title: string;
engagement: { time_on_page: number; scroll_depth: number; click_count: number };
experiments: Array<{ id: string; variant: string }>;
}