React Native SDK
The FeedbackKit React Native SDK adds feedback list and submit components, plus hooks, to React Native apps. Install the package, wrap your app in FeedbackProvider with your project API key, and mount the screens in your navigation.
Installation
Install the FeedbackKit React Native SDK along with its peer dependency feedbackkit-js using npm or yarn.
# Using npm
npm install feedbackkit-react-native feedbackkit-js @react-native-async-storage/async-storage
# Using yarn
yarn add feedbackkit-react-native feedbackkit-js @react-native-async-storage/async-storage
Note: feedbackkit-js and AsyncStorage are peer dependencies and must be installed separately. Peers are feedbackkit-js >=1.0.0, React >= 18, React Native >= 0.70, and AsyncStorage >= 1.17. Metrics hooks need feedbackkit-js >= 2.1.0, which is not on npm. The peer range stays open at the bottom so an app that only uses the feedback hooks can stay on 1.0.1. On a bare React Native app, run pod install so AsyncStorage's iOS side is linked.
Quick Start
Wrap your app with FeedbackProvider and start using the components and hooks.
import React from 'react';
import { FeedbackProvider, FeedbackList } from 'feedbackkit-react-native';
export default function App() {
return (
<FeedbackProvider
apiKey="your-api-key"
userId="user_12345"
>
<FeedbackList
onFeedbackPress={(feedback) => console.log(feedback.title)}
/>
</FeedbackProvider>
);
}
FeedbackProvider
The FeedbackProvider component wraps your app and provides the FeedbackKit context to all child components.
Props
| Prop | Type | Required | Description |
|---|---|---|---|
| apiKey | string | Yes | Your project API key |
| children | ReactNode | Yes | Your app components |
| userId | string | No | Current user. Persisted to AsyncStorage under @feedbackkit/userId |
| baseUrl | string | No | Defaults to the hosted API. https is required; http is only allowed for localhost |
| theme | FeedbackKitTheme | No | Partial theme, or the exported darkTheme |
| locale | string | No | Overrides device locale detection. Applied before children render. Not in npm 1.0.1 |
| translations | Record<string, string> | No | String overrides keyed by catalog ids. Not in npm 1.0.1 |
| bearerToken | string | No | Server credential for admin replies. Leave unset in an end-user app. Not in npm 1.0.1 |
Context Value
useFeedbackKitContext() returns the context below. useFeedbackKit() returns only the client. There is no projectId prop. The component name is FeedbackProvider.
| Property | Type | Description |
|---|---|---|
| client | FeedbackKit | The underlying FeedbackKit client |
| userId | string | undefined | Current user ID |
| setUserId | (id?: string) => void | Update user ID |
| theme | Theme | Merged theme. The prop is partial; the context value is complete |
| isInitialized | boolean | Whether the stored user id has finished loading |
| canPostAdminReply | boolean | True only when bearerToken was passed. The token itself stays on the client |
User ID and anonymous IDs
useVote, useSubmitFeedback, and useComments throw User ID is required… until the context id is set. Vote and submit throw before error is set. Comments store that failure on addError. List and detail reads still run with no id.
The provider does not mint an id. It loads @feedbackkit/userId from AsyncStorage when the userId prop is omitted, and it persists the prop and every setUserId call. A prop that arrives while the stored id is still loading is kept. Generate one stable id for anonymous users and pass it in. A new id on each launch lets the same person vote twice.
feedbackkit-js can mint local_<uuid> when a direct client call omits userId. On React Native that value is memory-only, the provider clears it when the context id is unset, and the hooks do not send it.
Hooks
The React Native SDK provides custom hooks for managing feedback, votes, and comments with built-in state management.
useFeedbackKitContext()
Returns the provider context: the client, the current user id, and setUserId. useFeedbackKit() returns only the client. Voting, submitting and commenting throw until a user id is set. The SDK does not create one.
const { client, userId, setUserId } = useFeedbackKitContext();
useFeedbackList(initialFilter?)
Fetches a list. The filter is status, category, includeMerged, and sort (FeedbackSort). There is no limit. setFilter accepts an object or an updater. A slower response that lands after a newer request is ignored. FeedbackSort is not in npm 1.0.1 or in npm feedbackkit-js 1.0.1.
const {
feedbacks,
isLoading,
error,
refetch,
filter,
setFilter
} = useFeedbackList({ status: FeedbackStatus.Pending });
useFeedback(feedbackId)
Fetches a single feedback item by ID with automatic refetching.
const { feedback, isLoading, error, refetch } = useFeedback('feedback-id');
useVote()
Votes and unvotes. Both email and notifyStatusChange are required for the status-change mail. Mailing-list fields are sent only when email is set. VoteButton does not send any of these fields. npm 1.0.1 already accepts email and notifyStatusChange. It does not accept the mailing-list fields.
const { vote, unvote, isVoting, error } = useVote();
// Vote for feedback. The user id comes from the provider.
await vote('feedback-id', {
email: 'user@example.com',
notifyStatusChange: true,
subscribeToMailingList: true,
mailingListEmailTypes: ['operational', 'marketing']
});
// Remove vote
await unvote('feedback-id');
useComments(feedbackId)
Fetches and adds comments. A failed send is stored on addError and does not replace comments. addError is not in npm 1.0.1.
const {
comments,
isLoading,
error,
addError,
refetch,
addComment,
isAdding
} = useComments('feedback-id');
// Add a comment. addComment takes the text, not an object.
await addComment('Great idea!');
// Admin reply. Throws, and does not call the API, unless bearerToken was set.
await addComment('Shipped in 2.4.', true);
useSubmitFeedback()
Submits new feedback. The creator is the first voter, so voteCount starts at 1. Mailing-list fields are sent only when userEmail is set. The Free cap of 10 items per project hides the row. It does not return 402.
const { submit, isSubmitting, error, clearError } = useSubmitFeedback();
const created = await submit({
title: 'Dark mode',
description: 'Please add dark mode',
category: FeedbackCategory.FeatureRequest,
userEmail: 'user@example.com',
subscribeToMailingList: true,
mailingListEmailTypes: ['operational']
});
// Free plan, eleventh item: the promise resolves. Later reads omit the row. Not a 402.
if (created.isHiddenOverLimit) {
console.log(created.id);
}
Components
Pre-built React Native components for common feedback UI patterns.
FeedbackList
Scrollable list with pull-to-refresh. Default sort is votes. showSortPicker defaults to true. sortBy and the picker are not in npm 1.0.1. emptyComponent replaces the empty state, including its submit button. A failed refresh keeps the rows already on screen.
| Prop | Type | Required |
|---|---|---|
| onFeedbackPress | (feedback: Feedback) => void | No |
| filterByStatus | FeedbackStatus | No |
| filterByCategory | FeedbackCategory | No |
| sortBy | FeedbackSort | No |
| showSortPicker | boolean | No |
| showAddButton | boolean | No |
| onAddPress | () => void | No |
| emptyComponent | ReactElement | No |
| ListHeaderComponent | ComponentType | ReactElement | No |
| style | ViewStyle | No |
| contentContainerStyle | ViewStyle | No |
FeedbackCard
Displays a single feedback item with vote button, status badge, and category.
| Prop | Type | Required |
|---|---|---|
| feedback | Feedback | Yes |
| onPress | (feedback: Feedback) => void | No |
| onVoteChange | (hasVoted: boolean, voteCount: number) => void | No |
| showStatus | boolean | No |
| showCategory | boolean | No |
| showVoteButton | boolean | No |
| showCommentCount | boolean | No |
| style | ViewStyle | No |
VoteButton
Votes with the provider's user id. It does not collect an email and does not set notifyStatusChange. The count updates after the server responds, and a later feedback prop replaces that count. Votable statuses are pending, approved, in_progress, and testflight.
| Prop | Type | Required |
|---|---|---|
| feedback | Feedback | Yes |
| onVoteChange | (hasVoted: boolean, voteCount: number) => void | No |
| showCount | boolean | No |
| size | 'small' | 'medium' | 'large' | No |
| style | ViewStyle | No |
SubmitFeedbackView
Form for a new item: category, title, description, optional email, and mailing-list opt-in. Not in npm 1.0.1. initialCategory defaults to feature request.
| Prop | Type | Required |
|---|---|---|
| onSubmit | (feedback: Feedback) => void | No |
| onCancel | () => void | No |
| initialCategory | FeedbackCategory | No |
| style | ViewStyle | No |
FeedbackDetailView
Full item with comments. Pass the non-null object from useFeedback. Not in npm 1.0.1. The vote button on this screen still does not send notifyStatusChange.
| Prop | Type | Required |
|---|---|---|
| feedback | Feedback | Yes |
| onVoteChange | (hasVoted: boolean, voteCount: number) => void | No |
| style | ViewStyle | No |
StatusBadge
Displays feedback status with color-coded badge.
| Prop | Type | Required |
|---|---|---|
| status | FeedbackStatus | Yes |
| size | 'small' | 'medium' | No |
CategoryBadge
Displays feedback category with icon and color.
| Prop | Type | Required |
|---|---|---|
| category | FeedbackCategory | Yes |
| size | 'small' | 'medium' | No |
Theming
Customize the appearance of FeedbackKit components by providing a custom theme to the FeedbackProvider.
FeedbackKitTheme Interface
| Property | Type | Description |
|---|---|---|
| primaryColor | string | Primary brand color |
| backgroundColor | string | Main background color |
| cardBackgroundColor | string | Card/surface color |
| textColor | string | Primary text color |
| secondaryTextColor | string | Secondary text color |
| borderColor | string | Border color |
| errorColor | string | Error state color |
| successColor | string | Success state color |
| statusColors | StatusColors | pending, approved, inProgress, testflight, completed, rejected. There is no warningColor |
| categoryColors | CategoryColors | featureRequest, bugReport, improvement, other |
| borderRadius | number | Default border radius |
| spacing | number | Base spacing unit |
Custom Theme Example
import { FeedbackKitTheme } from 'feedbackkit-react-native';
const darkTheme: FeedbackKitTheme = {
primaryColor: '#F59E0B',
backgroundColor: '#1F2937',
cardBackgroundColor: '#374151',
textColor: '#F9FAFB',
secondaryTextColor: '#9CA3AF',
borderColor: '#4B5563',
errorColor: '#EF4444',
successColor: '#10B981',
borderRadius: 12,
spacing: 16
};
<FeedbackProvider apiKey="..." theme={darkTheme}>
{...}
</FeedbackProvider>
Models & Types
The React Native SDK re-exports all models and types from feedbackkit-js. See the JavaScript SDK documentation for the complete type reference.
Core Types
-
•
Feedback- Complete feedback object with metadata -
•
FeedbackStatus- Enum: pending, approved, in_progress, testflight, completed, rejected -
•
FeedbackCategory- Enum: feature_request, bug_report, improvement, other. There is no question category -
•
Comment- Comment object with user info -
•
VoteResponse- Vote result with updated counts
Events
The components do not track views. useFeedbackKit().events.track does. eventName and userId are required. Property values must be strings, finite numbers, or booleans.
SDKEvents is feedback_list, feedback_detail, and submit_feedback. Any other name is a custom event. Custom events are stored on every plan. Below Pro they are omitted from a successful metrics read (meta.eventScope === 'predefined_only'). client.users.register({ userId, mrr }) records the id and an optional monthly amount. It has no email or name field.
const client = useFeedbackKit();
const userId = 'user_12345';
await client.events.track({
eventName: SDKEvents.FeedbackList,
userId,
properties: { source: 'settings' }
});
Metrics
Four hooks read the same API-key metrics family as feedbackkit-js >= 2.1.0. That client is not on npm, and these hooks are not in feedbackkit-react-native 1.0.1. Pass a query with a non-empty measures list and either days or a date pair. A clamped window is a successful response: read meta.retentionClamped and meta.retentionWindowDays. The windows are Free 30 days, Pro 90 days, and Team 365 days. summary.meta.granularity is omitted on the summary, not null. The event feed puts pagination on events.meta and the window block on events.metrics. Three reads throw PaymentRequiredError with code === 'METRICS_CUSTOM_EVENTS_REQUIRE_PRO' below Pro: a breakdown by event name, a custom eventName filter, and the raw event feed. Bearer-only measures throw ForbiddenError with code === 'METRICS_MEASURE_REQUIRES_BEARER'. On 14 October 2026 the production API answered 404 for /api/v1/metrics/summary, and the dev and testflight APIs answered 401 (the route exists).
useMetricsloads the summary.useMetricsTimeseries,useMetricsBreakdown, anduseMetricsEventsload the other three reads.
const { summary, error } = useMetrics({
measures: [MetricMeasure.FeedbackCount, MetricMeasure.EventCount],
days: 7
});
Error Handling
The React Native SDK uses the same error hierarchy as the JavaScript SDK. All of these extend FeedbackKitError. The missing-user-id failure from vote and submit is a plain Error, and it is not written to the hook's error.
| Error Class | Status Code | Description |
|---|---|---|
| FeedbackKitError | — | Base error class |
| AuthenticationError | 401 | Invalid API key |
| PaymentRequiredError | 402 | A subscription gate. Metrics uses code METRICS_CUSTOM_EVENTS_REQUIRE_PRO for the three custom-event reads. The Free feedback cap is not a 402 |
| ForbiddenError | 403 | Archived project, a closed vote, or a bearer-only measure (METRICS_MEASURE_REQUIRES_BEARER) |
| NotFoundError | 404 | Resource not found |
| ConflictError | 409 | Duplicate action |
| ValidationError | 400 | Request validation failed |
| RateLimitError | 429 | retryAfterSeconds when the server sent Retry-After. Metrics allows 60 requests / 60 seconds and the client does not retry |
| NetworkError | 0 | The request never completed |
| ServerError | 5xx | The server failed |
import {
AuthenticationError,
PaymentRequiredError,
NetworkError,
MetricMeasure,
useMetricsEvents
} from 'feedbackkit-react-native';
const { error } = useMetricsEvents({
measures: [MetricMeasure.EventCount],
days: 7
});
if (error instanceof AuthenticationError) {
// 401. Key missing, wrong, or regenerated.
} else if (error instanceof PaymentRequiredError &&
error.code === 'METRICS_CUSTOM_EVENTS_REQUIRE_PRO') {
// 402. The raw event feed on a plan without custom metrics.
} else if (error instanceof NetworkError) {
// The request never completed.
}
Guides
How to Add a Feedback Board to a React Native App
Add a votable feedback board to a React Native app with feedbackkit-react-native 1.0.1 from npm: provider, user ids, screens, navigation, MRR and theming.
12 min read
How to Collect Feature Requests in a Flutter or React Native App
Collect, vote on and close the loop on feature requests in a Flutter or React Native app, step by step, with FeedbackKit's SDKs.
7 min read