Feedback SDK for React Native and Expo: a feedback form, helpful / star / NPS rating prompts, and a headless API client for SeggWat.
- Pure TypeScript, zero native code, zero runtime dependencies.
- Works in Expo Go, the Expo managed workflow, and bare React Native — on iOS, Android, and web.
bun add @seggwat/react-native
# or: npm install @seggwat/react-nativereact and react-native are peer dependencies (already present in any RN/Expo
app).
Wrap your app in SeggWatProvider and drop a FeedbackButton anywhere inside
it:
import { SeggWatProvider, FeedbackButton } from '@seggwat/react-native';
export default function App() {
return (
<SeggWatProvider projectKey="your-project-key">
<YourApp />
<FeedbackButton />
</SeggWatProvider>
);
}That's it — the floating button opens a feedback form and submits to your project. Get your project key from the SeggWat dashboard.
All components read config and the API client from SeggWatProvider, so they
must be rendered inside it.
Floating button that opens the feedback form. Positioned via
options.buttonPosition; styled icon (default) or labeled.
<FeedbackButton />import { useSeggWat } from '@seggwat/react-native';
function HelpMenu() {
const { present } = useSeggWat();
return <Button title="Send feedback" onPress={present} />;
}Inline "Was this helpful?" card with Yes / No.
<HelpfulPrompt screenName="/docs/getting-started" onSubmitted={(r) => console.log(r.id)} />Inline star rating (1–maxStars, default 5).
<StarRatingPrompt maxStars={5} screenName="/checkout" />Inline 0–10 Net Promoter Score row.
<NpsPrompt screenName="/home" />Each prompt takes optional screenName and onSubmitted?: (r: RatingCreatedResponse) => void.
The provider renders the feedback form for you, so you normally never mount
FeedbackSheet yourself. Mount it only to attach a screen/path to submissions
from the built-in form — your instance then replaces the provider's default
one (still opened via present() or <FeedbackButton>):
<FeedbackSheet screenName="/settings" />Skip the components and talk to the API directly with SeggWatClient — useful
for custom UIs or background submissions.
import { SeggWatClient } from '@seggwat/react-native';
const client = new SeggWatClient({
projectKey: 'your-project-key',
appVersion: '1.4.0',
// apiUrl defaults to https://seggwat.com
});
// Feedback
await client.submitFeedback({ message: 'Love the new dashboard!', screenName: '/home' });
// Ratings (tagged union: helpful | star | nps)
await client.submitRating({ type: 'helpful', value: true }, { screenName: '/docs' });
await client.submitRating({ type: 'star', value: 4 }, { screenName: '/checkout' });
const res = await client.submitRating({ type: 'nps', value: 9 });
console.log(res.id, res.ratingType);Pass options to SeggWatProvider via the options prop:
<SeggWatProvider projectKey="..." options={{ buttonColor: '#111', buttonStyle: 'labeled' }}>| Option | Type | Default | Description |
|---|---|---|---|
buttonColor |
string |
"#3b7299" |
Accent color of the floating button and prompts. |
buttonPosition |
"bottomRight" | "bottomLeft" | "topRight" | "topLeft" |
"bottomRight" |
Floating button position. |
buttonStyle |
"icon" | "labeled" |
"icon" |
Circular icon or a labeled pill. |
theme |
"auto" | "light" | "dark" |
"auto" |
Color scheme; "auto" follows the device setting. |
appVersion |
string |
— | App version sent with each submission. |
appIdentifier |
string |
— | Sent as X-SeggWat-BundleID; add it to the project's Allowed Bundle IDs so native builds can submit (see below). |
language |
"en" | "de" | "sv" |
auto-detected | UI language; falls back to English. |
apiUrl |
string |
"https://seggwat.com" |
API base URL (point at localhost for dev). |
showPoweredBy |
boolean |
true |
Show "Powered by SeggWat" in the form. |
onSubmit |
(result: SubmitResult) => void |
— | Fires after every submission (success or failure). |
onSubmit receives { ok: true } on success or { ok: false, error } on
failure — a small addition over the iOS SDK, which only reports success.
On iOS and Android there is no browser Origin header, so a project whose
Allowed Origins are set to specific origins returns 403 to native
submissions.
Fix: set appIdentifier (sent as the X-SeggWat-BundleID header) and add that
same identifier to Allowed Bundle IDs in the project's dashboard settings,
next to Allowed Origins.
<SeggWatProvider projectKey="..." options={{ appIdentifier: 'com.acme.app' }}>Use your iOS bundle ID / Android applicationId — the value passed here must
match what you list in the dashboard.
Alternatively, add "*" to Allowed Origins, but that allows every web origin
too — not recommended.
Expo web / react-native-web builds send a normal browser Origin, so they
follow the origin allowlist instead of the bundle-ID list.
Attribute submissions to a signed-in user. Set it once on the provider, or update it later via the hook:
<SeggWatProvider projectKey="..." user={{ id: 'user-123', email: 'ada@example.com' }}>const { setUser } = useSeggWat();
setUser('user-123', 'ada@example.com'); // after login
setUser(); // clear on logoutid is sent as submitted_by, email as submitted_by_email.
Submissions throw a SeggWatError. Branch on error.code:
import { SeggWatError } from '@seggwat/react-native';
try {
await client.submitFeedback({ message });
} catch (err) {
if (err instanceof SeggWatError) {
switch (err.code) {
case 'rate_limited':
alert(`Please wait ${err.secondsRemaining}s and try again.`);
break;
case 'validation_failed':
alert(err.message);
break;
case 'network_error':
alert('Check your connection and try again.');
break;
default:
alert(err.message);
}
}
}Codes: invalid_project_key, validation_failed, rate_limited,
server_validation_failed, origin_not_allowed, project_not_found,
screenshot_too_large, unsupported_media_type, network_error,
server_error, invalid_rating_value, duplicate_rating.
Submissions are rate-limited to one every 10 seconds per client.
The feedback form can capture the screen and open a fullscreen annotation
editor (pen, arrow, rectangle, text, blackout — matching the iOS SDK and web
widgets). The SDK contains no native code, so this is powered by
react-native-view-shot
(bundled in Expo Go), which you install and pass in — apps that skip it keep a
dependency-free SDK and simply don't get the screenshot section:
bunx expo install react-native-view-shot # or: bun add react-native-view-shotimport * as ViewShot from 'react-native-view-shot';
<SeggWatProvider
projectKey="your-project-key"
options={{ screenshots: { viewShot: ViewShot, quality: 0.8 } }}
>With this set, the form shows a Screenshot button: the sheet hides, the screen behind it is captured, and the annotation editor opens. The annotated image is flattened and attached to the submission.
You can also capture a screenshot yourself and pass its file URI:
import { useRef } from 'react';
import { View } from 'react-native';
import { captureRef } from 'react-native-view-shot';
function ReportButton() {
const viewRef = useRef<View>(null);
const { submitFeedback } = useSeggWat();
async function report() {
const uri = await captureRef(viewRef, { format: 'jpg', quality: 0.8 });
await submitFeedback({
message: 'Something looks off on this screen',
screenshot: { uri }, // name/type default to screenshot.jpg / image/jpeg
});
}
return (
<View ref={viewRef} collapsable={false}>
{/* ...your screen... */}
</View>
);
}screenshot accepts { uri, name?, type? }. react-native-view-shot is an
optional peer dependency — install it only if you want screenshots.
Ships with English (en), German (de), and Swedish (sv). The language is
auto-detected from the device locale and falls back to English. Override it:
<SeggWatProvider projectKey="..." options={{ language: 'de' }}>- Website & dashboard: https://seggwat.com
MIT