Skip to content

ycedrick/react-native-press-guard

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

2 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

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-guard

Features

  • Double-tap prevention: Automatically locks your button until the async onPress promise 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, and unlock states.
  • 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 returned Promise resolves or rejects.
  • cooldown: Regardless of standard functions or promises, locks strictly for the cooldown ms duration after the tap.
  • hybrid: Wait for the promise to resolve, but wait at least the cooldown duration before unlocking.

πŸ“ License

MIT

About

Prevent accidental double taps or rapid presses in React Native and Expo apps. Supports async actions, cooldowns, progress tracking, and optional haptic feedback.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors