Engage
In-app messages, surveys, NPS and ratings rendered by one overlay — plus the frequency capping that stops it becoming spam.
Engage renders campaigns inside your app: in-app messages, surveys, NPS prompts, rating requests and coachmarks. One shared overlay renders all of them, so a new campaign type needs no app release.
Wrap your app#
import { EngagePromptProvider } from '@scalebun/react-native';
export default function App() { return ( <EngagePromptProvider> <NavigationContainer>{/* screens */}</NavigationContainer> </EngagePromptProvider> );}Place it inside any navigation container but outside your screens, so a prompt survives navigation and renders above whatever is on screen.
Trigger a prompt#
import { useEngagePrompt } from '@scalebun/react-native';
function OrderComplete() { const engage = useEngagePrompt();
useEffect(() => { // The backend decides whether anything actually shows. engage.showPrompt({ event: 'order_completed' }); }, []);}You are signalling that a moment has occurred — not commanding a prompt. Whether one appears depends on targeting, frequency caps and whether a campaign is live. That separation is the point: campaign timing is changed in the dashboard, not in a release.
Anchors and inline placement#
Coachmarks need to point at something. Register an anchor:
import { ScaleBunAnchor } from '@scalebun/react-native';
<ScaleBunAnchor id="filter-button"> <FilterButton /></ScaleBunAnchor>For a message that sits in your layout rather than floating over it:
import { ScaleBunInlineSlot } from '@scalebun/react-native';
<ScaleBunInlineSlot id="home-banner" />An inline slot renders nothing when no campaign targets it, so it is safe to leave in place permanently — no conditional wrapper needed.
Frequency capping#
Impressions, dismissals and completions are persisted on the device. This is what stops Engage becoming spam: a message marked show-once stays dismissed across launches and reinstalls of the JS bundle.
Seeing what the backend is serving#
const messages = await ScaleBun.engage.debugFetchInAppMessages();Forces a fresh config fetch and returns the in-app messages targeted at this device right now, including queued test previews. This is the fastest way to tell a targeting problem from a rendering problem: if the message is not in this list, the issue is targeting or eligibility, not your overlay.
Both of these are development helpers. Neither belongs on a release path.
Collecting responses#
Survey, NPS and feedback answers upload through the outbox, so they survive offline:
ScaleBun.engage.submitResponse({ campaignId: 'cmp_123', answers: [{ questionId: 'q1', value: 9 }],});With a screenshot or voice note attached:
ScaleBun.engage.submitResponseWithAttachments(response, attachments);Attachments are presigned and uploaded directly to storage, then the response is enqueued referencing them. If storage is disabled it degrades to a plain response rather than failing — the answer is worth more than the attachment.
Ratings#
ScaleBun.submitRating({ rating: 5, comment: 'Works well' });Rating campaigns can route a high rating to the platform store review prompt and a low one to a feedback form, so you are not asking unhappy users to review you publicly. That routing is configured in the dashboard.
Custom renderers#
The overlay resolves a renderer per campaign type from a registry, so you can replace one with your own component while leaving the rest alone:
import { PROMPT_RENDERERS, INAPP_RENDERERS } from '@scalebun/react-native';Only do this when your design system genuinely cannot be expressed through campaign configuration — a custom renderer is app code, so changing it needs a release, which gives back the main advantage of using Engage at all.
Related#
Push notifications — reaching users outside the app.
Troubleshooting — in-app messages not reappearing.