Skip to content

React Integration

The React integration provides a provider, hooks, and drop-in components for placement rendering and entitlement checks.

Wrap your app (or a subtree) with the provider:

React
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>
);
}
PropTypeDescription
optionsRevTurbineInitOptionsSDK initialization options (see Configuration Reference)
bootstrapPlacementsBootstrapPlacementInput[]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.
childrenReactNodeYour app

Pass colorScheme directly to the provider, outside options. See Theming for branding overrides and palette selection.

Preload placement decisions during initialization to eliminate loading flicker:

React
<RevTurbineProvider
options={options}
bootstrapPlacements={[
{ placement: { name: 'hero_banner' }, userId: 'user_123' },
{ placement: { name: 'nav_cta' }, userId: 'user_123' },
]}
>
<YourApp />
</RevTurbineProvider>

Access the SDK instance and provider state:

React
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:

FieldTypeDescription
sdkRevTurbineCustomerSdk | nullSDK instance (null until ready)
isReadybooleanWhether the SDK has initialized
errorstringError message if initialization failed
initStatusRevTurbineInitStatusInitialization 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) => voidUpdate 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.

Resolve a placement for a surface slot:

React
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:

FieldTypeDefaultDescription
surfaceSlotRevTurbineSurfaceSlotConfig—Slot configuration (id, surfaceTemplateIds)
userIdstring—Override user ID
contextMode'auto' | 'override''auto'Context resolution mode
traitsRecord<string, string | number | boolean>—Additional targeting traits
ttlMsnumber300000Cache TTL in milliseconds
autoLoadbooleantrueLoad placement on mount

Returns:

FieldTypeDescription
isLoadingbooleanWhether the placement is being resolved
errorstringError message if resolution failed
visiblebooleanWhether the placement should be shown
decisionRevTurbinePlacementDecision | nullFull decision object
contentobject | nullResolved 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

Check whether a user has access to a feature:

React
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:

FieldTypeDefaultDescription
handlestring—Entitlement handle (required)
autoCheckbooleantrueCheck on mount
autoGatebooleanfalseAuto-resolve gated placement if denied
gatePlacementRequestobject—Placement config for the gate

Returns:

FieldTypeDescription
isLoadingbooleanWhether the check is in progress
errorstring | nullError message
resultEntitlementResult | nullFull result
allowedbooleanAccess granted
limitedbooleanApproaching limit
deniedbooleanAccess denied
gatedPlacementPlacementOutput | nullUpgrade placement (if autoGate: true)
recheck() => Promise<void>Re-check entitlement

Get current usage balances:

React
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>
);
}

Access the resolved theme:

React
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>
);
}

For the simplest integration, use <Slot> — it resolves the placement and renders the matching built-in slot:

React
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.

Update the user context after login, plan change, or navigation:

React
const { setContext } = useRevTurbine();
// After login
setContext({
id: user.id,
plan_handle: user.planHandle,
custom: { role: user.role },
});

All active placements and entitlement checks re-evaluate automatically when context changes.