Add feedback and feature voting to a React Native app
Build a native feedback flow in React Native with a provider, bottom sheet, user identity, custom triggers, and production-ready testing.
This integration keeps feedback in the app your customer is already using. The default launcher opens a bottom sheet with a request form and voting board, backed by the same Upstep project as web and mobile clients.
Before you begin
- ✓A React Native or Expo application
- ✓A project API key from Upstep
- ✓react-native-safe-area-context installed in the app
Install the SDK and safe-area dependency
The sheet needs safe-area information to avoid notches, home indicators, and navigation bars. Install both packages before wiring the provider.
npm install @upstep/react-native react-native-safe-area-contextCheck before moving on
- □Rebuild the native app after adding a native dependency.
- □Use the same package manager your app already uses.
Wrap the application once
FeedbackProvider owns the API client and sheet state. Place it high enough that any signed-in screen can open feedback, but do not mount multiple providers for the same project.
import { FeedbackProvider, FeedbackButton, FeedbackSheet } from "@upstep/react-native";
export default function App() {
return (
<FeedbackProvider apiKey={process.env.EXPO_PUBLIC_UPSTEP_KEY!}>
<AppNavigator />
<FeedbackButton />
<FeedbackSheet />
</FeedbackProvider>
);
}Use a build-time environment variable
Expose only the Upstep project key to the client. Keep your database, payment, and authentication secrets server-side. For Expo, use an EXPO_PUBLIC variable; for bare React Native, inject configuration with your existing build configuration.
- Create a separate Upstep project for staging.
- Confirm the production key is set in the CI release environment.
# .env
EXPO_PUBLIC_UPSTEP_KEY=upstep_your_project_keyIdentify a customer after authentication
A stable internal ID lets the SDK associate feedback and deduplicate votes. The provider can receive userId initially, or you can identify a user later through the hook once your session has loaded.
import { useEffect } from "react";
import { useUpstep } from "@upstep/react-native";
function SyncFeedbackIdentity({ userId }: { userId?: string }) {
const { identify } = useUpstep();
useEffect(() => identify(userId), [identify, userId]);
return null;
}Open feedback from your own interface
The floating launcher is optional. Use the same sheet from a Settings row, help center, or error screen so feedback appears at the moment a customer has context to share.
import { useUpstep } from "@upstep/react-native";
function HelpRow() {
const { openSheet } = useUpstep();
return <Pressable onPress={openSheet}><Text>Send feedback</Text></Pressable>;
}Tune the default launcher
If you retain the default button, it can be positioned and labeled to match the app. Keep it clear of your tab bar and avoid using a label that competes with support or account actions.
<FeedbackButton
position="bottom-right"
label="Share feedback"
/>Test on physical devices
Use a real Android device and iPhone before release. Check keyboard behaviour, safe areas, anonymous submission, identified voting, and a slow network connection.
Check before moving on
- □The sheet does not sit behind a tab bar or home indicator.
- □Feedback creates an item in the intended project.
- □A vote updates and stays after reopening the sheet.
- □The app remains usable if the feedback API is temporarily unavailable.
Troubleshooting
The provider cannot find safe-area context.+
Ensure react-native-safe-area-context is installed and your app has its usual SafeAreaProvider where required.
The button opens but no feedback is shown.+
Confirm FeedbackSheet is mounted inside the same FeedbackProvider as the button.
The production build uses the wrong project.+
Check the release environment variable and use a dedicated staging project rather than sharing a production key during QA.