Touch is the sense we design for the least and notice the most. When a slider snaps into place with a tiny click, or a payment confirms with a satisfying double-thump, your thumb believes the interface in a way your eyes never quite do. react-native-haptic-feedback is the library that has, for years, given React Native apps that tactile vocabulary on both iOS and Android, and with its version 3 rewrite it grew from a five-buzz wrapper into a genuine haptic design toolkit.
The pitch is simple: one cross-platform API for vibration and haptic feedback, with no New Architecture requirement and no Expo lock-in. It works in bare React Native projects, ships zero runtime dependencies beyond its peers, and is downloaded around 455,000 times a week. Whether you want a single line that fires a "success" pulse or a hand-authored rhythmic pattern with millisecond timing, this library covers the spectrum. Let's dig in.
Why Touch Feedback Earns Its Place
Haptics are not decoration. They confirm actions without stealing visual attention, they communicate state changes for users who aren't looking directly at the screen, and they make interactions feel physical and trustworthy. A toggle that buzzes when it flips, a long-press that confirms it registered, a failed form submission that gives a short error rattle: these are the small touches that separate a polished app from a merely functional one.
Here is what react-native-haptic-feedback v3 brings to the table:
- 40+ predefined haptic types spanning impacts, notifications, selections, and lower-level gesture and keyboard effects.
- Custom intensity scaling from 0.0 to 1.0 via the
impactmethod. - Custom patterns with precise timing, intensity, and sharpness per event.
- Pattern notation, a compact string DSL for sketching rhythmic feedback quickly.
- AHAP file playback for Apple Haptic and Audio Pattern files on iOS, with cross-platform fallback.
- React integrations: a
useHapticshook and aTouchableHapticcomponent. - System awareness: query vibration availability and ringer mode, plus a library-wide on/off kill switch.
The genius of the design is that none of this complexity is in your way. The simplest call is still one line, and every richer feature is opt-in.
Getting It Into Your Project
Install the package with your manager of choice.
npm install react-native-haptic-feedback
Or with Yarn:
yarn add react-native-haptic-feedback
On iOS, run pod install from your ios/ directory as you would for any native module. If you are on React Native 0.71 or later, autolinking handles the rest, so there is no manual native configuration to wire up.
A quick note on platform minimums for v3: iOS 13.0 (for Core Haptics), Android API 23 (Android 6.0), and React Native 0.71.0. If you are upgrading from v2, those bumped minimums are the main thing to watch, which we will return to at the end.
Your First Buzz
The entry point you will reach for most often is trigger. Pass it a feedback type and the device does the rest.
import RNHapticFeedback from "react-native-haptic-feedback";
RNHapticFeedback.trigger("impactMedium");
That single call maps to the right native API on each platform: Core Haptics or UIKit feedback generators on iOS, and the appropriate VibrationEffect or HapticFeedbackConstants path on Android. You don't think about any of that.
The type string is where the expressiveness lives. Reach for semantic types that match the action:
// Confirm a successful payment or save
RNHapticFeedback.trigger("notificationSuccess");
// Warn the user about a risky action
RNHapticFeedback.trigger("notificationWarning");
// Signal a failed form submission
RNHapticFeedback.trigger("notificationError");
// A light tick as a picker value changes
RNHapticFeedback.trigger("selection");
As a rule of thumb: use impactLight, impactMedium, and impactHeavy for physical-feeling collisions like buttons and drags; use the notification* family for outcomes (success, warning, error); and use selection for value changes in pickers, sliders, and segmented controls. Matching the haptic to the meaning is what makes feedback feel intentional rather than noisy.
Respecting the User's Wishes
Every trigger call accepts a second options argument, and two flags matter in practice.
RNHapticFeedback.trigger("impactMedium", {
enableVibrateFallback: true,
ignoreAndroidSystemSettings: false,
});
enableVibrateFallback tells iOS to fall back to a plain vibration when Core Haptics isn't available (older devices), so feedback still happens. ignoreAndroidSystemSettings, when left at its default of false, respects the user's system-level vibration preference, which is usually the polite choice. Both default to false.
Beyond per-call options, the library gives you a global switch. If your app has a "haptics off" setting, you don't need to thread a condition through every call site:
// In your settings screen, when the user toggles haptics off
RNHapticFeedback.setEnabled(false);
// Anywhere, to check current state
const on = RNHapticFeedback.isEnabled();
While disabled, every haptic operation becomes a no-op, which keeps your interaction code clean. You can also query the device with getSystemHapticStatus() to learn whether vibration is available and inspect ringer information before deciding to fire anything.
Composing Custom Patterns
Predefined types cover most needs, but sometimes you want a signature feel: a heartbeat for a health app, a distinctive double-thump for your brand's confirmation. The triggerPattern method sequences haptic events with explicit timing, intensity, and sharpness.
import RNHapticFeedback from "react-native-haptic-feedback";
RNHapticFeedback.triggerPattern([
{ time: 0, type: "transient", intensity: 1.0, sharpness: 0.5 },
{ time: 150, type: "transient", intensity: 0.4, sharpness: 0.3 },
{ time: 400, type: "transient", intensity: 1.0, sharpness: 0.8 },
]);
Each event carries its own timing offset and dynamics, so you can sculpt the rhythm precisely. For common feelings, the library ships ready-made presets so you don't have to author them by hand:
import { Patterns } from "react-native-haptic-feedback";
RNHapticFeedback.triggerPattern(Patterns.success);
RNHapticFeedback.triggerPattern(Patterns.heartbeat);
RNHapticFeedback.triggerPattern(Patterns.tripleClick);
The built-in set includes success, error, warning, heartbeat, tripleClick, and notification, which together cover a lot of everyday UX without writing a single event by hand.
Sketching Rhythms With Pattern Notation
For quick experiments, hand-building event arrays is more ceremony than the idea deserves. The library includes a compact string notation that reads almost like sheet music for your thumb.
| Character | Meaning | Timing |
|---|---|---|
o |
Soft transient (intensity 0.4) | 100 ms |
O |
Strong transient (intensity 1.0) | 100 ms |
. |
Short gap | +150 ms |
- |
Medium gap | +400 ms |
= |
Long gap | +1000 ms |
So the string "oO.O" means: a soft tap, a strong tap, a short pause, then a strong tap. You build a pattern from that notation and play it like any other.
import RNHapticFeedback, { pattern } from "react-native-haptic-feedback";
// Soft, strong, pause, strong
RNHapticFeedback.triggerPattern(pattern("oO.O"));
// A galloping triple with a longer rest
RNHapticFeedback.triggerPattern(pattern("OoO-O"));
This is wonderful for iteration. You can tweak a string, reload, and feel the difference in seconds, then graduate to a full event array once you have settled on the rhythm.
Going Pro With AHAP and Cross-Platform Playback
On iOS, Apple's own haptic design format is the AHAP file (Apple Haptic and Audio Pattern), which designers can author and tune with precision. If you bundle a .ahap file in your app, you can play it directly:
RNHapticFeedback.playAHAP("MyCustomHaptic");
AHAP is iOS-only, but you rarely want to leave Android users with silence. The playHaptic wrapper bridges the gap: it uses your AHAP file on iOS and falls back to pattern notation on Android, so a single call produces a sensible feel on both platforms.
RNHapticFeedback.playHaptic("MyCustomHaptic", "oO.O", {
enableVibrateFallback: true,
});
If a haptic is running and you need to cut it short, stop() halts the engine and cancels playback. That matters for longer custom patterns tied to gestures the user can abandon mid-way.
React-First Ergonomics
Calling the API imperatively works fine, but React developers will appreciate the two integrations that make haptics feel native to component code.
The useHaptics hook lets a component establish default options once and then trigger with per-call overrides that merge on top.
import { useHaptics } from "react-native-haptic-feedback";
import { Pressable, Text } from "react-native";
function SaveButton({ onSave }: { onSave: () => void }) {
const haptics = useHaptics({ enableVibrateFallback: true });
return (
<Pressable
onPress={() => {
haptics.trigger("notificationSuccess");
onSave();
}}
>
<Text>Save</Text>
</Pressable>
);
}
For the common case of "fire a haptic when this is pressed," the TouchableHaptic component is a drop-in Pressable replacement that wires the feedback up for you. You choose the type and which gesture triggers it.
import { TouchableHaptic } from "react-native-haptic-feedback";
import { Text, StyleSheet } from "react-native";
function ConfirmButton({ onPress }: { onPress: () => void }) {
return (
<TouchableHaptic
hapticType="impactMedium"
hapticTrigger="onPressIn"
hapticOptions={{ enableVibrateFallback: true }}
onPress={onPress}
style={styles.button}
>
<Text>Confirm</Text>
</TouchableHaptic>
);
}
const styles = StyleSheet.create({
button: { padding: 16, borderRadius: 12 },
});
The hapticTrigger prop accepts "onPressIn", "onPress", or "onLongPress", which lets you tune exactly when the buzz fires relative to the touch. Firing on onPressIn tends to feel the most immediate and responsive, since the feedback arrives the instant the finger lands.
Keeping Tests Quiet
Native modules can be awkward in unit tests, but this one is mock-friendly. A single line gives you working stand-ins for every exported method and hook.
jest.mock("react-native-haptic-feedback");
That auto-mock keeps your component tests fast and silent, with no real native calls and no manual stubbing of each method.
Migrating From v2
If you are coming from version 2, the good news is that the basic trigger("impactMedium") API is unchanged, so simple existing usage keeps working untouched. The breaking changes are mostly about platform minimums: iOS now targets 13.0 (remove any older deployment-target overrides), React Native's minimum is now 0.71, the internal DeviceUtils class was removed, and the vibration fallback behavior shifted on devices that lack Core Haptics support. For most apps the upgrade is a dependency bump and a quick check of your iOS deployment target.
The Verdict
react-native-haptic-feedback occupies a sweet spot in the React Native ecosystem. It is the established, dependency-light default that anyone can drop into a bare project, yet v3 quietly leveled it up to match the flashier newcomers with Core Haptics, AHAP support, pattern notation, and proper React integrations. It doesn't demand the New Architecture, it doesn't tie you to Expo, and its one-line entry point means you can add a single satisfying buzz today and grow into hand-authored tactile design tomorrow.
Touch feedback is one of those features users never explicitly ask for but always feel. With a library this approachable, there is no reason to leave that channel silent. Start with a notificationSuccess on your next confirmation flow, feel the difference, and let your fingers do the convincing.