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 |
Keeping mounted gates current
Section titled “Keeping mounted gates current”Gate and useEntitlement subscribe to SDK context and usage notifications.
After rt.update({ usage: { api_calls: total } }) or a supported context update,
the mounted gate rechecks; a forced remount or page reload is not required.
recheck() remains available for an explicit retry.
This subscription does not poll your billing database. When a subscription or balance changes elsewhere, fetch fresh authoritative context and pass it to the SDK. Updating React state that the SDK never receives cannot change its decision. Preserve the provider’s stable options object and update facts through the SDK rather than rebuilding the provider for every balance change.
Verify a gate stays mounted while usage crosses its limit and then resets. Also verify a paid-to-free user switch clears the old grants. See identity lifecycle and usage periods.
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