SDK Reference/React Native
Framework
@swake/react-native

React Native

The official React Native SDK for Swake. Feedback forms, bug reporting, surveys, NPS, voting boards, notifications, and breadcrumbs — fully themeable and offline-first.

Overview

@swake/react-native is a feedback, bug-reporting, and survey SDK for React Native apps — a floating trigger button, feedback form, bug reporter with screenshot annotation, in-app voting board, and NPS/survey modals, all offline-first so nothing is lost on a flaky connection.

A few concepts come up throughout this guide:

ConceptWhat it is
apiKeyYour project's key, the only required option to Swake.init().
<SwakeProvider>Wraps your app and renders every Swake overlay — feedback form, bug reporter, survey modals, shake prompt, and the trigger FAB. Required for any Swake UI to appear.
identify()Links feedback, surveys, and notifications to a user. Almost everything downstream depends on it.
Offline queueSubmissions and replies are queued and retried automatically — persisted across app restarts if @react-native-async-storage/async-storage is installed.
ThemeA single theme object, referenced by nearly every widget, applied to the native UI.

Getting Started

Requirements

DependencyMinimum version
React Native>= 0.73
React>= 18
TypeScript (recommended)>= 5

Installation

bash
npm install @swake/rn-swake-sdk lucide-react-native react-native-svg
# or
yarn add @swake/rn-swake-sdk lucide-react-native react-native-svg
# or
pnpm add @swake/rn-swake-sdk lucide-react-native react-native-svg

Quick Start

Two things are required: call Swake.init() once at app startup, and wrap your component tree with <SwakeProvider>.

App.tsx
import Swake, { SwakeProvider } from '@swake/react-native';

// 1. Initialise before any component renders
Swake.init({
  apiKey: 'ep_live_your_key_here',
});

export default function App() {
  return (
    // 2. Wrap your app so modals and overlays can render
    <SwakeProvider>
      <NavigationContainer>
        <RootNavigator />
      </NavigationContainer>
    </SwakeProvider>
  );
}

<SwakeProvider> renders the feedback form modal, bug reporter, survey overlays, shake prompt, and the floating trigger button. It must be an ancestor of any screen where you want Swake UI to appear.

Full init options

Everything except apiKey is optional.

typescript
Swake.init({
  apiKey: 'ep_live_your_key_here',
  baseUrl: 'https://api.swake.io',       // custom API endpoint
  debug: false,                          // verbose logging
  appVersion: '2.1.0',                   // auto-detected if omitted

  // Shake gesture
  shakeToReport: true,
  shakeThreshold: 2.5,                  // g-force
  shakeCooldown: 10000,                 // ms between prompts

  // Floating trigger button
  trigger: { /* see Trigger Button section */ },

  // Surveys
  surveys: { /* see Surveys & NPS section */ },

  // Breadcrumbs
  breadcrumbs: { /* see Breadcrumbs section */ },

  // Global theme
  theme: { /* see Theme Customization section */ },
});
Full reference:

Identifying Users

Call identify() after the user signs in. This links all feedback, notifications, and survey responses to their account. Call clearIdentity() on logout.

src/auth/onLogin.ts
import Swake from '@swake/react-native';

async function onLoginSuccess(user: User) {
  await Swake.identify({
    userId: user.id,              // required — your internal user ID
    email: user.email,            // optional but recommended
    name: user.name,              // optional
    metadata: {                   // optional — custom traits
      plan: 'pro',
      company: 'Acme Corp',
    },
  });
}

function onLogout() {
  Swake.clearIdentity();
}

email and name are preserved server-side if omitted on subsequent calls. metadata is fully replaced on every call.


Feedback

Feedback Form

Open the built-in feedback form modal. It includes a type selector, title and description fields, and file attachment support.

typescript
// Open with defaults
Swake.showFeedbackForm();

// Open with options
Swake.showFeedbackForm({
  defaultType: 'feature',         // pre-select a type
  showTypeSelector: true,         // show bug / idea / help tabs
  title: "What's on your mind?",  // header text
  placeholder: 'Tell us more...', // description placeholder
  theme: { primaryColor: '#0EA5E9' },  // per-call theme override
});

You can also open the form via the imperative shorthand:

typescript
// Opens the feedback form (equivalent to showFeedbackForm)
Swake.open();

// Opens the bug reporter instead
Swake.open('bug');

Bug Reporter

The bug reporter captures a screenshot, lets the user annotate it (draw, rectangles, ellipses), and submits it alongside the feedback form — all in one flow.

For the bug reporter (screenshot + annotation), install both react-native-view-shot and react-native-svg.

typescript
// Open the bug reporter
Swake.showBugReporter();

// With a theme override
Swake.showBugReporter({
  theme: { primaryColor: '#EF4444' },
});

Required dependencies: react-native-view-shot (screenshot capture) and react-native-svg (annotation canvas). The SDK falls back to a regular feedback form if either is missing.

Shake to report

When shakeToReport is enabled (the default), shaking the device shows a prompt asking the user if they want to report a bug. Accepting opens the bug reporter.

typescript
// Enabled by default in init:
Swake.init({
  apiKey: '...',
  shakeToReport: true,   // default
  shakeThreshold: 2.5,   // g-force sensitivity
  shakeCooldown: 10000,   // ms between prompts
});

// Toggle at runtime
Swake.setShakeToReport(false); // disable temporarily

Submitting Feedback

Submit feedback from your own custom UI. The submission is queued offline-first and synced automatically when connectivity is available.

typescript
import Swake from '@swake/react-native';

const submission = await Swake.submitFeedback({
  type: 'bug',           // 'bug' | 'feature' | 'question'
  title: 'Checkout button unresponsive',
  description: 'Tapping the checkout button on iOS 17 does nothing.',
  attachments: [         // optional — max 5 files
    { uri: photo.uri, filename: 'screenshot.png', mimeType: 'image/png' },
  ],
});

console.log('Created:', submission.id);

Trigger Button (FAB)

The SDK renders a floating action button (FAB) that opens the feedback form on tap and the bug reporter on long-press. Configure it in init().

typescript
Swake.init({
  apiKey: '...',
  trigger: {
    position: 'bottom-right',   // 'bottom-left' | 'top-right' | 'top-left'
    icon: 'chat',               // 'feedback' | 'bug' | 'idea' | { uri: '...' }
    label: 'Feedback',          // optional — makes it an extended FAB
    hidden: false,              // start hidden, show later
    topOffset: 0,               // additional spacing from top (when position is 'top-*')
    bottomOffset: 0,            // additional spacing from bottom (when position is 'bottom-*')
    style: {
      backgroundColor: '#6366F1',
      size: 56,                 // diameter in dp
      borderRadius: 28,         // half of size = circle
      shadow: true,
    },
  },
});

// Show / hide at runtime
Swake.setTriggerVisible(false);
Swake.setTriggerVisible(true);
PositionDescription
bottom-rightBottom-right corner (default)
bottom-leftBottom-left corner
top-rightTop-right corner (respects safe area)
top-leftTop-left corner (respects safe area)
{ x, y }Absolute pixel position

Avoiding overlaps with navigation

Use topOffset and bottomOffset to add extra spacing when your app has bottom tab bars, navigation headers, or other fixed UI elements. The FAB automatically respects safe areas and adds these offsets on top.

typescript
// Example: FAB positioned above React Navigation bottom tabs
Swake.init({
  apiKey: '...',
  trigger: {
    position: 'bottom-right',
    bottomOffset: 60,  // tab bar height + some padding
  },
});

// Example: FAB below a custom top header
Swake.init({
  apiKey: '...',
  trigger: {
    position: 'top-right',
    topOffset: 50,  // header height
  },
});

Recommended values: For standard React Navigation tab bars, use bottomOffset: 60 (iOS) or bottomOffset: 70 (Android). For headers, use topOffset: 44-60 depending on your header design.


Engagement

Surveys & NPS

Surveys are configured in the Swake portal and delivered to users who match targeting rules. After identify(), the SDK automatically checks eligibility and shows the first matching survey.

typescript
Swake.init({
  apiKey: '...',
  surveys: {
    enabled: true,          // enable survey rendering (default: true)
    autoShow: true,         // show after identify() (default: true)
    delay: 3000,            // ms before presenting (default: 3000)
    cooldown: 86_400_000,   // min ms between surveys (default: 24h)
  },
});

Manual trigger

typescript
// Re-check eligibility and show if a survey matches
await Swake.checkSurveys();

// Force-show a specific survey by ID (bypasses cooldown)
await Swake.showSurvey('surv_01HW...');

Event callbacks

typescript
Swake.onSurveyShown(({ id, type }) => {
  console.log('Survey shown:', type, id);
});

Swake.onSurveyCompleted(({ survey, response }) => {
  console.log('Completed', survey.type, response.answers.length, 'answers');
});

Swake.onSurveyDismissed(({ id }) => {
  console.log('Dismissed:', id);
});

Inline polls

Embed a single-question poll directly in your UI. Shows percentage results after voting. Poll questions can be single-choice (a tap casts the vote) or multi-select (multiple_choice) — the widget adapts automatically based on the question configured in the portal, letting the user pick several options (up to the per-question limit) and submit with a Vote button.

typescript
import { PollWidget } from '@swake/react-native';

<PollWidget
  surveyId="surv_01HW..."  // optional — auto-picks first eligible if omitted
  onVote={(index) => console.log('Voted option', index)}
  onDismiss={() => console.log('Poll dismissed')}
/>

Whether a poll is single- or multi-select is set per question in the portal (via the option-selection limit) — no SDK flag needed. For a multi-select question, onVote fires once with the first selected option's index.


Voting Board

Open a public voting board as a native in-app screen — item list with upvote buttons, rendered from the public board API. Users can browse and vote on items. The SDK uses the identified user's identity automatically if identify() was called.

typescript
// Open the native board UI (no WebView needed)
Swake.openVotingBoard('public-roadmap', {
  theme: { colorScheme: 'dark' },  // optional
});

The board is fully native — no react-native-webview is needed. Browsing works anonymously; casting a vote requires identify().

Programmatic voting

Build your own voting UI by fetching items and casting votes via the API.

typescript
// Fetch board items
const { items, hasMore } = await Swake.getBoardItems('public-roadmap', {
  sort: 'votes',   // 'votes' | 'recent'
  page: 1,
});

// Cast a vote (requires identify)
const result = await Swake.vote('public-roadmap', items[0].id);
// { status: 'voted', voteCount: 42 }

// Remove a vote
await Swake.removeVote('public-roadmap', items[0].id);

Notifications

Notifications

Users receive in-app notifications when a team member replies to their feedback or changes a status. Use these methods to build a notification inbox or badge.

typescript
import Swake from '@swake/react-native';

// Badge count
const unread = await Swake.getUnreadCount();

// List notifications
const { notifications, pagination } = await Swake.listNotifications({
  unreadOnly: true,
  limit: 20,
});

// Mark as read
await Swake.markNotificationRead(notifications[0].id);
await Swake.markAllNotificationsRead();

Push tokens

Register device push tokens for server-sent notifications. Call after identify() and after the user grants permission.

typescript
// Register
await Swake.registerPushToken('apns', deviceToken);  // iOS
await Swake.registerPushToken('fcm', deviceToken);   // Android
await Swake.registerPushToken('expo', expoPushToken); // Expo

// Unregister on logout
await Swake.unregisterPushToken(deviceToken);

Customization

Theme Customization

Set a global theme in init() to brand every Swake widget — the feedback form, bug reporter, surveys, voting board, and the floating trigger button.

App.tsx
Swake.init({
  apiKey: '...',
  theme: {
    primaryColor: '#8B5CF6',
    successColor: '#10B981',
    dangerColor: '#EF4444',
    backgroundColor: '#FFFFFF',
    surfaceColor: '#F9FAFB',
    overlayColor: 'rgba(0,0,0,0.5)',
    textColor: '#111827',
    textSecondary: '#6B7280',
    borderColor: '#E5E7EB',
    borderRadius: 12,
    fontFamily: 'Inter',
    colorScheme: 'light',   // 'light' | 'dark' | 'system'
  },
});

Token reference

TokenDefaultDescription
primaryColor#6366F1Buttons, active states, FAB background
successColor#10B981Success checkmark, resolved badges
dangerColor#EF4444Error borders, destructive actions
backgroundColor#FFFFFFModal / panel background
surfaceColor#F9FAFBInput fields, secondary surfaces
overlayColorrgba(0,0,0,0.45)Backdrop behind modals
textColor#111827Primary text
textSecondary#6B7280Labels, placeholders, metadata
borderColor#E5E7EBInput borders, dividers
borderRadius8Corner radius for inputs and buttons
fontFamilySystem defaultFont family applied to all text
colorSchemesystemLight / dark mode (React Native only)

Per-call overrides

Every UI method (showFeedbackForm, showBugReporter, openVotingBoard, etc.) accepts a theme parameter that merges on top of the global theme for that single call.

typescript
Swake.showFeedbackForm({
  theme: { primaryColor: '#0EA5E9' },
});

Swake.openVotingBoard('roadmap', {
  theme: { colorScheme: 'dark' },
});

Merge order: SDK defaults → global init() theme → per-call theme. Only the tokens you set are overridden.



Offline

Offline Support

All feedback submissions, comments, and identify calls are automatically queued when the device is offline and synced when connectivity returns. Install @react-native-async-storage/async-storage for persistence across app restarts.

typescript
import { offlineQueue } from '@swake/react-native';

// Check queue size
console.log('Pending items:', offlineQueue.size);

// Force flush (sends everything now)
await offlineQueue.flush();

// Clear all queued items
await offlineQueue.clear();

AI Setup

Using Claude Code

Prefer to let an AI coding agent do the wiring? Run Claude Code from the root of your React Native project and paste the prompt below. It reads these docs as the source of truth, detects your stack, asks you for your API key and which peers to install, then wires up the provider, init(), identify(), breadcrumbs, and the native setup — in one shot.

Prerequisites

Before you run the prompt, set up your project in the Swake portal and grab the values the agent will ask for — it only writes placeholders to your env files, so you supply the real values yourself.

  1. Sign in to the Swake portal and create a project (or open an existing one).
  2. In Project settings → API keys, get existing or create a key and copy it — it's shown once and starts with ep_live_.
  3. If you want the in-app voting board, create a board and copy its slug from the board's URL / settings.

Have these ready when the agent prompts you — the same values you'd otherwise wire up by hand in the Quick Start:

ValueWhere to find itExample
apiKeyProject settings → API keysep_live_…
baseUrlAPI endpoint — optional, only needed when self-hosting or routing through your own backendhttps://api.swake.io
boardSlugVoting board settings — optional; passed to openVotingBoard() / vote(), not init()public-roadmap

The prompt is additive and safe: a Swake or network error must never break your app's boot or existing behavior. The agent will pause and ask before installing packages or writing secrets, and it never guesses APIs that aren't in these docs.

The prompt

Run Claude Code from your project root and paste this:

Claude prompt
Fully integrate the Swake SDK (feedback infra: forms, bug reports, surveys, notifications, voting boards). Additive: a Swake/network error must NEVER break boot or existing behavior. Never guess or fake APIs.

1. FIRST read the docs (source of truth for package, peers, init/identify, provider, native setup). Fetch with a browser User-Agent (a plain fetch returns 403), e.g. curl -sL -A "Mozilla/5.0" "https://swake.io/docs/sdk?framework=react-native" (match the framework param). Then detect the stack; if no matching SDK exists, STOP and report.
2. ASK the user (AskUserQuestion tool) for API key (ep_live_...) + base/portal URL + board slug → env + .env.example (placeholders only). Then ASK to confirm which peers to install: required (lucide-react-native, react-native-svg) + optional per feature (async-storage, view-shot, shake, image-picker, webview). Install approved only.
3. Mount the provider once at the app root, above navigation.
4. Call init() once, idempotently. With onboarding, set surveys.autoShow:false + init lazily on the first main screen. Theme→your palette; re-init on dark toggle.
5. identify() with a stable ID + metadata; clearIdentity() on logout.
6. Route every call via one wrapper (try/catch + configured/initialized guard); feed breadcrumbs. Native: iOS usage strings + pods; strip Android ACTIVITY_RECOGNITION.
7. Verify on Android AND iOS release builds: a real submission hits the dashboard; no surveys over onboarding. Report changes, manual steps, assumptions.

Reference

Full API Reference

Click any method for its full signature, parameters, and an example.

MethodDescription
Initialise the SDK (call once)
Set the current user identity
Clear identity and wipe queue
Submit feedback programmatically
Open the feedback form modal
Open the bug reporter with screenshot
Open the submission list screen
Open a submission detail screen
Open feedback form or bug reporter
Close any open Swake UI
Open a voting board in a WebView
Cast a vote on a board item
Remove a vote
Fetch board items
Re-check survey eligibility
Force-show a survey by ID
Get unread notification count
List notifications
Mark a notification as read
Mark all notifications as read
Register push token
Unregister push token
Show/hide the FAB
Toggle shake detection
Add a custom breadcrumb
Read the breadcrumb trail
Clear all breadcrumbs
Wire React Navigation for breadcrumbs
Fetch a targeted ad
Clear cached ads
Start a new replay capture window
Stop capture and discard the buffer
Returns true when actively recording
Tear down all listeners

Event callbacks

CallbackFires when
A survey is displayed
User completes a survey
User dismisses a survey
Workspace hits a plan limit

Optional Dependencies

Peer packages needed for specific features — the SDK degrades gracefully if any are missing.

FeaturePackage
Offline queue persistence@react-native-async-storage/async-storage
Shake to reportreact-native-shake (or expo-sensors)
Screenshot capturereact-native-view-shot
Screenshot annotationreact-native-svg
In-app voting boardreact-native-webview
Trigger button iconslucide-react-native
Haptic feedbackexpo-haptics