Haptics in React Native: Creating a useHaptic()
Hook
Hey, Fellow React Native Developers!
I think you would have used an app that gave you a really satisfying vibration or haptic feedback when you interacted with any Tappable Components, like when you pressed a button or completed a task or something.
Haptic feedback adds a lot to the UX of an app. Adding haptics into your app has become really easy with a couple of libraries out there.
And another one is from Expo.
In this blog post, we are going to use the expo-haptics
and we’ll be seeing how to create a common custom React hook, useHaptic()
that can handle the haptic feedback throughout your app.
I hope by the end of this blog, you will have a clear understanding of how to create a common functionality as hooks. You can check out a similar blog on “Creating a common Scale Animation Handler Hook”
Well, let us dive in and create our hook!
We will be using expo-haptics
.
It works on all platforms except on the Web.
Installation
npx expo install expo-haptics
If you are installing a bare React Native App, you need to go through some additional installation steps. You can check it out here.
General Usage
import * as Haptics from 'expo-haptics';
// In component
<Button
title="Success"
onPress={
() =>
Haptics.notificationAsync(
Haptics.NotificationFeedbackType.Success
)
}
/>
Creating the Hook
Defining Required Types for the Hook
From the docs, you can see there are about seven Feedback types for the Haptic. So that will be our first type.
type FeedbackType =
| "light"
| "medium"
| "heavy"
| "selection"
| "success"
| "warning"
| "error";
And these types are grouped mainly into two (well three) types:
- ImpactFeedbackStyle
- light
- medium
- heavy - NotificationFeedbackType
- success
- warning
- error - Selection
- selection
We will create separate handlers for the ImpactFeedbackStyle
& NotificationFeedbackType
.
Both handlers will be very similar.
const createHapticHandler = useCallback(
(type: Haptics.ImpactFeedbackStyle) => {
return Platform.OS === "web"
? undefined
: () => Haptics.impactAsync(type);
},
[],
);
const createNotificationFeedback = useCallback(
(type: Haptics.NotificationFeedbackType) => {
return Platform.OS === "web"
? undefined
: () => Haptics.notificationAsync(type);
},
[],
);
Both the handler's returns undefined
which means that the function doesn't do anything, because we don't have support for the web.
It returns the actual expo-haptics
respective function, like for ImpactFeedbackStyle
its impactAsync()
and for NotificationFeedbackType
it is notificationAsync()
with the type
.
These functions are created using the useCallback
to make sure that they are only created only once during the component’s lifecycle and are not recreated unnecessarily on each render.
Using the above-created handlers we create an object mapping each haptic feedback type to its respective handler functions.
We create this object using useMemo()
that ensures it is only created once during the component’s lifecycle.
const hapticHandlers = useMemo(
() => ({
light: createHapticHandler(Haptics.ImpactFeedbackStyle.Light),
medium: createHapticHandler(Haptics.ImpactFeedbackStyle.Medium),
heavy: createHapticHandler(Haptics.ImpactFeedbackStyle.Heavy),
selection: Platform.OS === "web" ? undefined : Haptics.selectionAsync,
success: createNotificationFeedback(
Haptics.NotificationFeedbackType.Success,
),
warning: createNotificationFeedback(
Haptics.NotificationFeedbackType.Warning,
),
error: createNotificationFeedback(Haptics.NotificationFeedbackType.Error),
}),
[createHapticHandler, createNotificationFeedback],
);
The object’s properties are:
light
,medium
, andheavy
: These are mapped to thecreateHapticHandler()
one we have already created, corresponding to theImapactFeedbackStyle
. This is to handle haptic for impacts of different strengths.selection
: Mapeped toHaptics.selectionAsync()
and undefined if the Platform is Web.success
,warning
,error
: These are mapped to thecreateNotificationFeedback()
corresponding toNotificationFeedbackType
. This is to handle haptic for different types of notifications.
Let us now piece it all together to get one useHaptic()
.
import { useCallback, useMemo } from "react";
import { Platform } from "react-native";
import * as Haptics from "expo-haptics";
type FeedbackType =
| "light"
| "medium"
| "heavy"
| "selection"
| "success"
| "warning"
| "error";
export const useHaptic = (feedbackType: FeedbackType = "selection") => {
const createHapticHandler = useCallback(
(type: Haptics.ImpactFeedbackStyle) => {
return Platform.OS === "web"
? undefined
: () => Haptics.impactAsync(type);
},
[],
);
const createNotificationFeedback = useCallback(
(type: Haptics.NotificationFeedbackType) => {
return Platform.OS === "web"
? undefined
: () => Haptics.notificationAsync(type);
},
[],
);
const hapticHandlers = useMemo(
() => ({
light: createHapticHandler(Haptics.ImpactFeedbackStyle.Light),
medium: createHapticHandler(Haptics.ImpactFeedbackStyle.Medium),
heavy: createHapticHandler(Haptics.ImpactFeedbackStyle.Heavy),
selection: Platform.OS === "web" ? undefined : Haptics.selectionAsync,
success: createNotificationFeedback(
Haptics.NotificationFeedbackType.Success,
),
warning: createNotificationFeedback(
Haptics.NotificationFeedbackType.Warning,
),
error: createNotificationFeedback(Haptics.NotificationFeedbackType.Error),
}),
[createHapticHandler, createNotificationFeedback],
);
return hapticHandlers[feedbackType];
};
Well, this is our hook! Locked and loaded to be used inside our components.
We can consume our hook in different ways based on our use case.
If we need our haptic to let a user know when a selection change has been registered. We can use the default selection
.
const hapticSelection = useHaptic();
// In component onPress
hapticSelection()
The hook defaults to selection
and returns Haptics.selectionAsync()
.
You can customize it to give a specific feedback type by passing in any one of the FeedbackType
.
Well, this brings us to the end of this blog.
With the above-created hook, we can now easily access and provide haptic feedback to the user throughout the app, without having to write repetitive code.
I hope you found this blog resourceful and that it improves your development process in some way.
Check out my blogs on popular UI Interactions with React Native here.



I am trying to be more consistent to write more about working with Reanimated and Gesture Handler Libraries to achieve said UI Interactions. Follow and stay tuned!
If you find this blog post helpful, tell me in the comments, would love to know your views. Thank you :)
If you find any difficulties or see anything which could be done better, please feel free to add your comments (I really like to read them, maybe something on what my next blog should be? You know. Whatev! ). … :)