react-native-press-guard
v1.0.2
Published
A React Native & Expo package to prevent double taps and rapid presses. Features async locks, cooldown tracking, and optional haptic feedback.
Maintainers
Readme
react-native-press-guard
A fully type-safe React Native & Expo package to gracefully prevent double taps, rapid presses, and manage async button states. Supports lock modes, cooldowns, progress tracking, and optional built-in haptic feedback.
Zero dependencies*. (*Optionally integrates dynamically with expo-haptics).
Installation
npm install react-native-press-guard
# or
yarn add react-native-press-guardFeatures
- Double-tap prevention: Automatically locks your button until the async
onPresspromise resolves. - Cooldown mode: Prevent rapid tapping by setting a custom millisecond cooldown.
- Hybrid mode: Wait for the promise to resolve, then enforce the cooldown.
- Drop-in Component: Use
<PressGuard>exactly like a React Native<Pressable>. - Custom Hook: Build your own guarded components using
usePressGuard. - HOC Wrapper: Wrap existing components with
withPressGuard(YourComponent). - Render Props: Exposes
isLocked,progress, andunlockstates. - Optional Haptics: Automatically uses
expo-haptics(if installed in your app) for rich feedback on press, success, and error events.
🚀 Quick Start: <PressGuard> Component
<PressGuard> is a drop-in replacement for the standard <Pressable>.
import { PressGuard } from "react-native-press-guard";
import { Text } from "react-native";
export default function App() {
const submitData = async () => {
await fetch("https://api.example.com/submit", { method: "POST" });
};
return (
<PressGuard
onPress={submitData}
mode="promise"
haptics={{ press: "Light", success: "Success" }}
style={({ pressed }) => [{ opacity: pressed ? 0.7 : 1 }]}
>
{({ isLocked }) => <Text>{isLocked ? "Submitting..." : "Submit"}</Text>}
</PressGuard>
);
}🎣 The usePressGuard Hook
If you want more control, use the hook directly inside your own custom button.
import React from "react";
import { TouchableOpacity, Text, View } from "react-native";
import { usePressGuard } from "react-native-press-guard";
export const MySafeButton = ({ onPress }) => {
const {
onPress: guardedPress,
isLocked,
progress,
} = usePressGuard(onPress, {
mode: "cooldown",
cooldown: 2000,
haptics: { press: "Medium" },
});
return (
<TouchableOpacity onPress={guardedPress} disabled={isLocked}>
<Text>Tap Me</Text>
{isLocked && (
<View
style={{
height: 4,
width: `${progress * 100}%`,
backgroundColor: "red",
}}
/>
)}
</TouchableOpacity>
);
};📦 HOC: withPressGuard
You can wrap any component that accepts an onPress and disabled prop.
import { Button } from "react-native";
import { withPressGuard } from "react-native-press-guard";
// Now it's perfectly safe!
const SafeButton = withPressGuard(Button, { mode: "promise" });
export default () => (
<SafeButton title="Press Me" onPress={async () => await doWork()} />
);🛠 Advanced Usage
⚙️ Progress Animation
When using cooldown or hybrid mode, usePressGuard (and <PressGuard>) exposes a progress value between 0 and 1. This uses requestAnimationFrame to give you butter-smooth updates for building cooldown pie-charts, loaders, or progress bars.
<PressGuard onPress={syncAction} mode="cooldown" cooldown={3000}>
{({ isLocked, progress }) => (
<View style={{ opacity: isLocked ? 0.5 : 1 }}>
<Text>Send Message</Text>
{isLocked && <Text>Wait {Math.round((1 - progress) * 3)}s</Text>}
</View>
)}
</PressGuard>💫 Haptic Feedback (Optional)
If your app has expo-haptics installed (npx expo install expo-haptics), you can trigger haptic feedback automatically at different lifecycle stages.
Options available:
'Light' | 'Medium' | 'Heavy' | 'Success' | 'Warning' | 'Error'
<PressGuard
onPress={asyncAction}
haptics={{
press: "Medium", // Triggered immediately when pressed
success: "Success", // Triggered when promise resolves
error: "Error", // Triggered if promise rejects
cooldownStart: "Light", // Triggered when cooldown phase begins
cooldownEnd: "Light", // Triggered when button is unlocked
}}
>
<Text>Haptic Button</Text>
</PressGuard>API Reference
UsePressGuardOptions
| Property | Type | Default | Description |
| ----------- | ------------------------------------- | ----------- | ----------------------------------------------------------- |
| mode | 'promise' \| 'cooldown' \| 'hybrid' | 'promise' | Behavior mode. |
| cooldown | number | 1000 | Cooldown duration in ms. Native to cooldown and hybrid. |
| disabled | boolean | false | Disable the press guard completely. |
| haptics | object | {} | Haptic feedback configuration object map. |
| onSuccess | (res: T) => void | undefined | Callback fired when promise successfully resolves. |
| onError | (err: Error) => void | undefined | Callback fired when promise rejects or handler throws. |
mode Types
promise: Unlocks automatically when the returnedPromiseresolves or rejects.cooldown: Regardless of standard functions or promises, locks strictly for thecooldownms duration after the tap.hybrid: Wait for the promise to resolve, but wait at least thecooldownduration before unlocking.
📝 License
MIT
