Adding Haptic Feedback to Your Expo App: the Short Answer
Adding haptic feedback to your Expo app is one package and three methods. Install expo-haptics with npx expo install expo-haptics, then call the method that matches the moment: Haptics.selectionAsync() when a selection changes under the user's finger, Haptics.impactAsync() with a weight of Light, Medium, Heavy, Soft, or Rigid when something is tapped or a gesture crosses a threshold, and Haptics.notificationAsync() with Success, Warning, or Error when an operation finishes. No config plugin, no native setup - the module handles the platform permission on Android for you. The work is not the wiring. The work is deciding which interactions deserve feedback at all, and that decision is what separates an app that feels engineered from one that buzzes like a toy.
Haptics matter to indie apps for a simple reason: they are one of the few polish signals a user feels in the first ten seconds without being able to name it. A toggle that clicks under the thumb, a pull-to-refresh that ticks at the release point, a payment confirmation that lands with a soft double tap - these read as native quality. And because so few first apps bother, doing it well is cheap differentiation.
Step 1 - Install expo-haptics
Install the package with npx expo install expo-haptics so the version is pinned to your Expo SDK. There is nothing to add to app.json: on iOS the module talks to the Taptic Engine directly, and on Android it declares the vibration permission for you. Import it where you need it and call the methods inline.
- Run npx expo install expo-haptics in your project.
- Import it in the component that owns the interaction: import * as Haptics from 'expo-haptics'.
- Call the matching method inside the same handler that updates your UI - onPress, onValueChange, the gesture callback - so the physical feedback and the visual change land as one event.
- Do not gate your logic on the haptic. Fire it and move on; if the device cannot render it, the call resolves quietly and your app behaves identically.
Step 2 - Match the Method to the Moment
expo-haptics gives you three vocabularies, and using the right one is most of the craft. Selection is the lightest - a subtle tick meant for values changing under the finger, like scrolling a picker wheel or sliding across segmented options. Impact is for collisions - a tap on a button, a card snapping into place, a drag crossing its trigger threshold - and its five styles (Light, Medium, Heavy, Soft, Rigid) let you scale the weight to the size of the event. Notification is for outcomes - an operation that succeeded, needs attention, or failed - and its three types render as small patterned sequences rather than single taps.
Which expo-haptics method fits which interaction
| Method | Feels like | Use it for |
|---|---|---|
| selectionAsync() | A faint tick | Picker wheels, segmented controls, sliders snapping between steps, any value changing under the finger |
| impactAsync(Light) | A soft tap | Ordinary button presses, toggling a switch, small UI elements |
| impactAsync(Medium / Heavy) | A firmer thud | Larger commitments - confirming an action, a card or sheet snapping into its resting place |
| impactAsync(Soft / Rigid) | Rounder or sharper versions of the tap | Tuning feel to match your UI's character once the basics work |
| notificationAsync(Success) | A quick double tap | Payment confirmed, upload finished, task completed |
| notificationAsync(Warning / Error) | A distinct attention pattern | Validation failures, a destructive action about to happen, an operation that did not go through |
One rule ties the table together: the weight of the haptic should match the weight of the event. A Heavy impact on every keystroke is exhausting; a Light tap on a completed purchase undersells the moment. When in doubt, go one step lighter than your instinct - restraint is what reads as premium.
The Haptic Map: Decide Where Feedback Belongs Before You Wire It
Inside App Store Launch Club we call this the Haptic Map - a one-page list of your app's interactions with a haptic assigned to each, written before you touch the code. The point is editorial, not technical: haptics fail by inflation. The first one you add feels great, so you add ten more, and suddenly the whole app vibrates like a slot machine. Mapping first forces the cut.
- List the moments a user physically commits to something: confirming, deleting, paying, completing. These get notification haptics - they are outcomes.
- List the direct-manipulation surfaces: toggles, pickers, sliders, drag gestures with a snap point. These get selection or Light impact - they are textures.
- Everything else gets nothing. Navigation taps, list scrolling, typing, screen transitions - the platform already handles what needs handling.
- For each entry, write the visible feedback that accompanies it. If you cannot name one, the haptic is carrying information alone, and it will be lost on every user who has haptics turned off.
Let Claude Code Wire the Map in One Pass
Once the Haptic Map exists, the implementation is exactly the kind of mechanical, many-small-edits task Claude Code clears in one pass from the desktop app. Paste the map and ask it to wire each entry: it installs expo-haptics, adds the imports, and drops the right call into each handler alongside the existing UI update. Because the map names both the interaction and the method, there is no ambiguity for it to guess at.
- Have Claude Code install expo-haptics and wire each mapped interaction with the method your map assigns.
- Ask it to add a single settings toggle that gates all haptic calls behind one user preference, so users who dislike vibration can turn yours off without digging into system settings.
- Tell it to keep every haptic call fire-and-forget - no awaits blocking UI updates, no logic depending on the haptic having played.
- Then test on a real device yourself, walking the map entry by entry and confirming each moment feels proportionate.
That last step is not optional, and not just because of the simulator. Android motors vary widely between devices - the same impactAsync call that feels crisp on an iPhone can feel mushy or loud on a budget Android phone, and only your hands can tell you. If something misbehaves on hardware, [how to debug your Expo app on a real device](/blog/how-to-debug-expo-app-on-device) walks the diagnosis.
Common Haptic Feedback Mistakes and How to Avoid Them
Haptic problems are rarely bugs. They are almost always taste errors or hardware assumptions, and each has a mechanical fix.
Common haptic feedback mistakes in an Expo app
| Mistake | What goes wrong | Fix |
|---|---|---|
| Testing in the iOS simulator | Every call is silent and the feature seems broken | The simulator has no Taptic Engine - test on a physical device |
| Haptics on every tap | The app feels like a toy and users disable haptics or delete the app | Run the Haptic Map: outcomes and direct manipulation get feedback, navigation and typing get nothing |
| Using haptics as the only signal | Users with haptics off, or an iPhone in Low Power Mode, miss the information entirely | Every haptic accompanies visible feedback; none carries meaning alone |
| One weight for everything | Big moments undersell and small moments overwhelm | Scale the style to the event - selection for ticks, Light for taps, notification patterns for outcomes |
| Assuming Android feels like iOS | A call tuned on iPhone feels coarse or buzzy on some Android hardware | Verify on a real Android device and prefer lighter styles where the motor is rough |
| Awaiting the haptic before updating UI | Visual feedback lags the touch | Fire-and-forget: trigger the haptic and render the change in the same handler |
TL;DR
Add haptic feedback to your Expo app by installing expo-haptics with npx expo install - no config plugin needed - and calling the method that matches the moment: selectionAsync for values changing under the finger, impactAsync in one of five weights for taps and snap points, notificationAsync in Success, Warning, or Error for outcomes. Decide placement before wiring with a Haptic Map: outcomes and direct manipulation earn feedback, everything else gets nothing, and every haptic accompanies a visible change so nothing depends on a motor the user may have turned off. Test on real hardware - the iOS simulator renders nothing, and Android motors vary by device. Claude Code wires the whole map in one pass; your job is the editorial cut and the on-device feel test.
Haptics are one of those details that quietly separate a shipped app from a finished one - the same category as the splash screen hand-off covered in [how to add a splash screen to your Expo app](/blog/how-to-add-a-splash-screen-to-your-expo-app). Inside App Store Launch Club, builders trade exactly this kind of polish note before they submit, and get their builds felt and reviewed by people who have already shipped. Join at applaunchclub.co for $9 a month and bring your build.
Short, practical drops on app ideas, validating fast, store listings, and passing app review. No spam, unsubscribe anytime.
Frequently asked questions
Why does my Expo haptic feedback not work in the simulator?
Because haptics are physical hardware output and the iOS simulator has no Taptic Engine to render them. Your code can be wired perfectly and the simulator will still produce nothing. Run the app on a physical iPhone or Android device to feel the output - that is the only way to verify haptics at all.
Do I need any permissions or config to use expo-haptics?
No manual setup. Install it with npx expo install expo-haptics and import it. On Android the module declares the vibration permission on your behalf, and on iOS no permission is required for haptic output. There is no config plugin to add to app.json.
What is the difference between impactAsync, notificationAsync, and selectionAsync?
selectionAsync is a faint tick for values changing under the finger, like a picker wheel. impactAsync is a single tap for collisions - button presses, cards snapping into place - with five styles from Light to Heavy plus Soft and Rigid variants. notificationAsync plays a short pattern rather than a single tap, in Success, Warning, or Error flavors, and is meant for the outcome of an operation.
Why do haptics feel different on Android than on iOS?
iPhones render haptics through the Taptic Engine, which produces distinct, precise sensations for each style. Android devices use vibration motors that vary widely in quality between manufacturers and price tiers, so the same call can feel crisper, mushier, or louder depending on the phone. Test on real Android hardware and lean toward lighter styles where the motor is coarse.
Can users turn off my app's haptic feedback?
Users can disable haptics system-wide in their device settings, and iOS also suppresses many haptics in Low Power Mode - both outside your control. This is why no haptic should ever carry information alone: pair every one with visible feedback. It is also worth shipping an in-app toggle that gates your haptic calls, so a user who dislikes vibration can quiet your app specifically without losing system behavior elsewhere.
How many haptics should my app have?
Fewer than your first instinct. Reserve them for outcomes - success, warning, failure - and for direct-manipulation surfaces like toggles, pickers, and gestures with a snap point. Navigation taps, scrolling, and typing get nothing. An app that vibrates on every touch reads as a toy; a handful of well-placed haptics at commitment moments reads as native quality.
Last reviewed by David on September 22, 2026


