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

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors