Skip to content

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.

iOS Android React Native 0.70+ GitHub
npm install feedbackkit-react-native feedbackkit-js @react-native-async-storage/async-storage

Installation

Install the FeedbackKit React Native SDK along with its peer dependency feedbackkit-js using npm or yarn.

terminal
# 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.

App.tsx
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.

hooks.tsx
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.

FeedbackScreen.tsx
const {
    feedbacks,
    isLoading,
    error,
    refetch,
    filter,
    setFilter
} = useFeedbackList({ status: FeedbackStatus.Pending });

useFeedback(feedbackId)

Fetches a single feedback item by ID with automatic refetching.

DetailScreen.tsx
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.

VoteButton.tsx
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.

CommentsScreen.tsx
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.

SubmitScreen.tsx
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

theme.ts
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.

events.tsx
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).

  • useMetrics loads the summary.
  • useMetricsTimeseries, useMetricsBreakdown, and useMetricsEvents load the other three reads.
MetricsScreen.tsx
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
error-handling.tsx
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.
}