React Integration
The React integration provides a provider, hooks, and drop-in components for placement rendering and entitlement checks.
RevTurbineProvider
Section titled “RevTurbineProvider”Wrap your app (or a subtree) with the provider:
import { RevTurbineProvider, RuntimeMode } from '@revturbine/sdk';import playbook from './revturbine.playbook.json';import { useMemo } from 'react';
export default function App() { const options = useMemo(() => ({ runtimeMode: RuntimeMode.LocalOnly, // evaluate the bundled Playbook locally localRuntime: { playbook }, user: { id: 'user_123', plan_handle: 'starter', }, }), []);
return ( <RevTurbineProvider options={options}> <Dashboard /> </RevTurbineProvider> );}| Prop | Type | Description |
|---|---|---|
options | RevTurbineInitOptions | SDK initialization options (see Configuration Reference) |
bootstrapPlacements | BootstrapPlacementInput[] | Placements to preload on mount |
colorScheme | 'light' | 'dark' | 'system' | Placement palette; defaults to 'system' and follows the device preference. Changing this prop does not reinitialize the SDK. |
children | ReactNode | Your app |
Pass colorScheme directly to the provider, outside options. See Theming for branding overrides and palette selection.
Bootstrap Placements
Section titled “Bootstrap Placements”Preload placement decisions during initialization to eliminate loading flicker:
<RevTurbineProvider options={options} bootstrapPlacements={[ { placement: { name: 'hero_banner' }, userId: 'user_123' }, { placement: { name: 'nav_cta' }, userId: 'user_123' }, ]}> <YourApp /></RevTurbineProvider>useRevTurbine
Section titled “useRevTurbine”Access the SDK instance and provider state:
import { useRevTurbine } from '@revturbine/sdk';
function StatusBar() { const { sdk, isReady, initStatus, colorScheme } = useRevTurbine();
if (!initStatus.ok) { return ( <div role="alert"> <p>SDK initialization failed during {initStatus.phase}: {initStatus.message}</p> <p>{initStatus.remediation}</p> </div> ); } if (!isReady || !sdk) return <span>Loading SDK…</span>;
return <span>SDK ready · {colorScheme} theme</span>;}Returns:
| Field | Type | Description |
|---|---|---|
sdk | RevTurbineCustomerSdk | null | SDK instance (null until ready) |
isReady | boolean | Whether the SDK has initialized |
error | string | Error message if initialization failed |
initStatus | RevTurbineInitStatus | Initialization outcome, available even when sdk is null: { ok, phase?, message?, remediation? } |
colorScheme | 'light' | 'dark' | Resolved placement palette; 'system' has already been resolved. Set the provider prop to change it. |
setContext | (ctx: RevTurbineUserContext) => void | Update user context dynamically |
Read initStatus before treating a missing SDK as loading. On failure, ok is false and the phase, message and remediation describe what to fix. During initialization, ok is still true; wait for isReady and a non-null sdk before making checks. Mount this diagnostic inside your app’s actual provider so it tests the options and Playbook your app uses.
These fields are available from SDK 0.8.0. Development failure banners and theme warnings work in published packages from 0.8.9; initStatus remains available in production and in the earlier 0.8.x packages. See Troubleshooting when initialization succeeds but a placement does not render.
usePlacement
Section titled “usePlacement”Resolve a placement for a surface slot:
import { usePlacement } from '@revturbine/sdk';
function UpgradeBanner() { const { visible, content, isLoading, dismiss, ctaClick, } = usePlacement({ surfaceSlot: { id: 'dashboard_banner', surfaceTemplateIds: ['banner_placement'] }, });
if (!visible) return null;
return ( <div> <h3>{content?.header}</h3> <p>{content?.body}</p> <button onClick={() => ctaClick()}>{content?.cta_label}</button> <button onClick={() => dismiss()}>✕</button> </div> );}Options:
| Field | Type | Default | Description |
|---|---|---|---|
surfaceSlot | RevTurbineSurfaceSlotConfig | — | Slot configuration (id, surfaceTemplateIds) |
userId | string | — | Override user ID |
contextMode | 'auto' | 'override' | 'auto' | Context resolution mode |
traits | Record<string, string | number | boolean> | — | Additional targeting traits |
ttlMs | number | 300000 | Cache TTL in milliseconds |
autoLoad | boolean | true | Load placement on mount |
Returns:
| Field | Type | Description |
|---|---|---|
isLoading | boolean | Whether the placement is being resolved |
error | string | Error message if resolution failed |
visible | boolean | Whether the placement should be shown |
decision | RevTurbinePlacementDecision | null | Full decision object |
content | object | null | Resolved content (header, body, cta_label, etc.) |
refresh | () => Promise<void> | Re-resolve the placement |
dismiss | (cooldownMs?) => Promise<void> | Dismiss and suppress |
snooze | (seconds?) => Promise<void> | Temporarily suppress |
remindMeLater | (seconds?) => Promise<void> | Alias for snooze |
ctaClick | (target?) => Promise<void> | Record CTA click |
ctaComplete | (target?) => Promise<void> | Record CTA completion |
useEntitlement
Section titled “useEntitlement”Check whether a user has access to a feature:
import { useEntitlement } from '@revturbine/sdk';
function ExportButton() { const { allowed, denied, gatedPlacement, recheck } = useEntitlement({ handle: 'data_export', autoGate: true, // auto-resolve upgrade placement if denied });
if (denied && gatedPlacement) { return <UpgradePrompt placement={gatedPlacement} />; }
return <button disabled={!allowed}>Export Video</button>;}Options:
| Field | Type | Default | Description |
|---|---|---|---|
handle | string | — | Entitlement handle (required) |
autoCheck | boolean | true | Check on mount |
autoGate | boolean | false | Auto-resolve gated placement if denied |
gatePlacementRequest | object | — | Placement config for the gate |
Returns:
| Field | Type | Description |
|---|---|---|
isLoading | boolean | Whether the check is in progress |
error | string | null | Error message |
result | EntitlementResult | null | Full result |
allowed | boolean | Access granted |
limited | boolean | Approaching limit |
denied | boolean | Access denied |
gatedPlacement | PlacementOutput | null | Upgrade placement (if autoGate: true) |
recheck | () => Promise<void> | Re-check entitlement |
useUsageSnapshot
Section titled “useUsageSnapshot”Get current usage balances:
import { useUsageSnapshot } from '@revturbine/sdk';
function UsagePanel() { const { usage, refresh } = useUsageSnapshot();
return ( <div> <p>API calls: {usage.api_calls?.current} / {usage.api_calls?.limit}</p> <button onClick={refresh}>Refresh</button> </div> );}useRevTurbineTheme
Section titled “useRevTurbineTheme”Access the resolved theme:
import { useRevTurbineTheme } from '@revturbine/sdk';
function ThemedCard({ children }) { const theme = useRevTurbineTheme();
return ( <div style={{ background: theme.colors.surface, borderRadius: theme.shape.borderRadius, color: theme.colors.text, }}> {children} </div> );}Drop-In Components
Section titled “Drop-In Components”For the simplest integration, use <Slot> — it resolves the placement and renders the matching built-in slot:
import { Slot } from '@revturbine/sdk';
export function Surfaces() { return ( <> {/* Banner */} <Slot id="hero_banner" />
{/* Modal gate */} <Slot id="export_gate" />
{/* Inline card */} <Slot id="pricing_card" /> </> );}See the Component Gallery for interactive demos of every built-in slot component.
Dynamic Context Updates
Section titled “Dynamic Context Updates”Update the user context after login, plan change, or navigation:
const { setContext } = useRevTurbine();
// After loginsetContext({ id: user.id, plan_handle: user.planHandle, custom: { role: user.role },});All active placements and entitlement checks re-evaluate automatically when context changes.
Next Steps
Section titled “Next Steps”- Entitlements Guide — feature gates, usage limits, and credits
- Placements Guide — slot-based and entitlement-based patterns
- Theming Guide — customize slot component appearance
- Interactive Playground — try live editable SDK scenarios