Back to Components
📳
Native Haptics
AdvancedA lightweight, zero-dependency implementation of haptic feedback using direct native code for iOS (UIImpactFeedbackGenerator) and Android (Vibrator/HapticFeedbackConstants). Perfect for avoiding heavy external libraries.
Utilityv1.0.0Updated 2026-02-04
Installation
1
Copy NativeHaptics.ts to your project
2
Copy android/HapticsModule.kt & HapticsPackage.kt to android/app/src/main/java/com/yourpackage/haptics/
3
Copy ios/Haptics.swift & Haptics.m to ios/YourAppName/
4
Link the package in MainApplication.kt (Android)
5
Ensure Swift bridging header exists (iOS)
⚠️ Native Setup Required:
- No npm install required!
Source Code
NativeHaptics.tsTS
import { NativeModules, Platform } from 'react-native';
const { Haptics } = NativeModules;
// Safety check
if (!Haptics) {
console.warn('[Haptics] Native module not linked');
}
// Low-level wrappers
const impact = (style: 'light' | 'medium' | 'heavy' | 'soft' | 'rigid' = 'medium') => {
Haptics?.impact(style);
};
const selection = () => {
Haptics?.selection();
};
const notification = (type: 'success' | 'warning' | 'error' = 'success') => {
Haptics?.notification(type);
};
// Utils
export const triggerHaptic = () => {
// subtle feedback (welcome icons, light UI)
impact(Platform.OS === 'ios' ? 'soft' : 'light');
};
export const triggerSelectionHaptic = () => {
// perfect for tabs, pickers
selection();
};
export const triggerMediumHaptic = () => {
// buttons, bottom nav, primary actions
impact('medium');
};
export const triggerSuccessHaptic = () => {
notification('success');
};
export const triggerErrorHaptic = () => {
notification('error');
};
android/HapticsModule.ktKT
package com.travelogger.haptics
import android.content.Context
import android.os.Build
import android.os.VibrationEffect
import android.os.Vibrator
import android.view.HapticFeedbackConstants
import android.view.View
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
import com.facebook.react.bridge.ReactMethod
class HapticsModule(
private val reactContext: ReactApplicationContext
) : ReactContextBaseJavaModule(reactContext) {
override fun getName(): String = "Haptics"
private fun vibrate(duration: Long, amplitude: Int) {
val vibrator =
reactContext.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
vibrator.vibrate(
VibrationEffect.createOneShot(duration, amplitude)
)
} else {
@Suppress("DEPRECATION")
vibrator.vibrate(duration)
}
}
@ReactMethod
fun impact(style: String) {
when (style) {
"light" -> vibrate(10, 50)
"medium" -> vibrate(20, 120)
"heavy" -> vibrate(30, 255)
else -> vibrate(15, 100)
}
}
@ReactMethod
fun selection() {
val activity = reactContext.currentActivity ?: return
activity.window.decorView.performHapticFeedback(
HapticFeedbackConstants.KEYBOARD_TAP,
HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING
)
}
@ReactMethod
fun notification(type: String) {
when (type) {
"success" -> vibrate(40, 180)
"warning" -> vibrate(60, 200)
"error" -> vibrate(80, 255)
}
}
}
android/HapticsPackage.ktKT
package com.travelogger.haptics
import com.facebook.react.ReactPackage
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.uimanager.ViewManager
class HapticsPackage : ReactPackage {
override fun createNativeModules(
reactContext: ReactApplicationContext
): List<NativeModule> {
return listOf(HapticsModule(reactContext))
}
override fun createViewManagers(
reactContext: ReactApplicationContext
): List<ViewManager<*, *>> = emptyList()
}
ios/Haptics.swiftSWIFT
import UIKit
@objc(Haptics)
class Haptics: NSObject {
@objc static func requiresMainQueueSetup() -> Bool {
return true
}
@objc func impact(_ style: NSString) {
DispatchQueue.main.async {
let feedbackStyle: UIImpactFeedbackGenerator.FeedbackStyle
switch style {
case "light":
feedbackStyle = .light
case "medium":
feedbackStyle = .medium
case "heavy":
feedbackStyle = .heavy
case "soft":
if #available(iOS 13.0, *) {
feedbackStyle = .soft
} else {
feedbackStyle = .light
}
case "rigid":
if #available(iOS 13.0, *) {
feedbackStyle = .rigid
} else {
feedbackStyle = .medium
}
default:
feedbackStyle = .medium
}
let generator = UIImpactFeedbackGenerator(style: feedbackStyle)
generator.prepare()
generator.impactOccurred()
}
}
@objc func selection() {
DispatchQueue.main.async {
let generator = UISelectionFeedbackGenerator()
generator.prepare()
generator.selectionChanged()
}
}
@objc func notification(_ type: NSString) {
DispatchQueue.main.async {
let generator = UINotificationFeedbackGenerator()
generator.prepare()
switch type {
case "success":
generator.notificationOccurred(.success)
case "warning":
generator.notificationOccurred(.warning)
case "error":
generator.notificationOccurred(.error)
default:
generator.notificationOccurred(.success)
}
}
}
}
ios/Haptics.mM
#import <React/RCTBridgeModule.h>
@interface RCT_EXTERN_MODULE(Haptics, NSObject)
RCT_EXTERN_METHOD(impact:(NSString *)style)
RCT_EXTERN_METHOD(selection)
RCT_EXTERN_METHOD(notification:(NSString *)type)
@end
Usage Examples
Basic Usage
Trigger haptics on button press
Example 1
import { triggerSelectionHaptic } from './NativeHaptics';
<TouchableOpacity onPress={triggerSelectionHaptic}>
<Text>Press Me</Text>
</TouchableOpacity>Props
| Prop | Type | Default | Description |
|---|---|---|---|
triggerHaptic | function | - | Triggers a light/soft impact |
triggerSelectionHaptic | function | - | Triggers a selection feedback (good for pickers/tabs) |
triggerSuccessHaptic | function | - | Triggers a success notification pattern |
triggerErrorHaptic | function | - | Triggers an error notification pattern |
Features
- Zero npm dependencies
- Direct Native Access
- iOS High Precision Haptics
- Android Vibration Fallback
- Type-safe Wrapper
- Custom patterns supported
Dependencies
Required:
None