npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

react-native-otp-timer-hook

v0.0.2

Published

A customizable OTP timer hook and UI component with background support for React Native.

Downloads

6

Readme

react-native-otp-timer

A powerful, customizable, and performance-optimized OTP (One-Time Password) timer component for React Native applications. Perfect for implementing secure authentication flows with automatic resend functionality and background time tracking.

Features

  • ⏱️ Accurate Timing: Precise countdown with background time calculation
  • 🔄 Auto Resend: Configurable resend attempts with loading states
  • 📱 Background Aware: Continues timing when app is backgrounded
  • 🎨 Fully Customizable: Extensive styling and text formatting options
  • Performance Optimized: Built with React.memo and stable references
  • 🔧 TypeScript Support: Fully typed with comprehensive interfaces
  • 🧪 Testing Friendly: Debug mode and testing utilities included
  • 🎯 Flexible API: Use as component or hook only
  • 🚀 Zero Dependencies: No external dependencies except React Native

Installation

npm install react-native-otp-timer
# or
yarn add react-native-otp-timer

demo. demo

Quick Start

import React from 'react';
import { View, Alert } from 'react-native';
import OtpTimer from 'react-native-otp-timer';

const App = () => {
  const handleResend = async (attemptNumber: number) => {
    // Your OTP resend logic here
    console.log(`Resending OTP - attempt ${attemptNumber}`);
    await fetch('/api/resend-otp', { method: 'POST' });
  };

  const handleTimeout = () => {
    Alert.alert('Timeout', 'OTP has expired');
  };

  return (
    <View style={{ flex: 1, justifyContent: 'center', padding: 20 }}>
      <OtpTimer
        initialTimer={300} // 5 minutes
        resendLimit={3}
        onResend={handleResend}
        onTimeout={handleTimeout}
      />
    </View>
  );
};

export default App;

Advanced Usage

Fully Customized Implementation

import React, { useCallback } from 'react';
import { StyleSheet } from 'react-native';
import OtpTimer from 'react-native-otp-timer';

const CustomOtpTimer = () => {
  const handleResend = useCallback(async (count: number) => {
    try {
      const response = await api.resendOtp({ attempt: count });
      showSuccess('OTP sent successfully');
    } catch (error) {
      showError('Failed to send OTP');
      throw error; // Prevent timer reset on failure
    }
  }, []);

  const formatTime = useCallback((seconds: number) => {
    const mins = Math.floor(seconds / 60);
    const secs = seconds % 60;
    return `${mins}m ${secs}s remaining`;
  }, []);

  return (
    <OtpTimer
      initialTimer={600} // 10 minutes
      resendLimit={5}
      onResend={handleResend}
      onTimeout={() => navigation.goBack()}
      onLimitExceeded={() => showContactSupport()}
      containerStyle={styles.container}
      textStyle={styles.text}
      linkStyle={styles.link}
      formatText={formatTime}
      formatResendText={(attempt, max) => `Resend (${attempt}/${max})`}
      showAttemptCounter={true}
      debug={__DEV__}
    />
  );
};

const styles = StyleSheet.create({
  container: {
    backgroundColor: '#f8f9fa',
    padding: 16,
    borderRadius: 8,
  },
  text: {
    fontSize: 16,
    color: '#495057',
  },
  link: {
    fontSize: 16,
    color: '#007bff',
    fontWeight: '600',
  },
});

Hook-Only Usage

import { useOtpTimer } from 'react-native-otp-timer';

const CustomTimerComponent = () => {
  const { timer, resetTimer, isExpired, pauseTimer, resumeTimer } = useOtpTimer({
    initialTimer: 300,
    onTimeout: () => console.log('Timer expired'),
    onTick: (currentTime) => {
      if (currentTime === 60) {
        showWarning('1 minute remaining!');
      }
    },
    debug: true,
  });

  // Your custom UI implementation
  return (
    <View>
      <Text>Time: {timer}s</Text>
      <Button title="Reset" onPress={resetTimer} />
    </View>
  );
};

API Reference

OtpTimer Component Props

| Prop | Type | Default | Description | | ---------------------- | ------------------------------------------ | --------------------- | --------------------------------------- | | initialTimer | number | Required | Initial countdown time in seconds | | resendLimit | number | Required | Maximum number of resend attempts | | onResend | (count: number) => Promise<void> | Required | Callback for handling OTP resend | | onTimeout | () => void | undefined | Called when timer reaches zero | | onLimitExceeded | () => void | undefined | Called when resend limit is exceeded | | onTimerStart | (seconds: number) => void | undefined | Called when timer starts/resets | | containerStyle | ViewStyle | undefined | Custom container styling | | textStyle | TextStyle | undefined | Custom text styling | | linkStyle | TextStyle | undefined | Custom link/button styling | | limitExceededStyle | TextStyle | undefined | Custom style for limit exceeded message | | formatText | (seconds: number) => string | "MM:SS" | Custom timer display formatter | | formatResendText | (attempt: number, max: number) => string | "Resend" | Custom resend button text | | limitExceededMessage | string | "Limit exceeded..." | Custom limit exceeded message | | enabled | boolean | true | Enable/disable timer functionality | | showAttemptCounter | boolean | false | Show attempt counter in UI | | debug | boolean | false | Enable debug logging |

useOtpTimer Hook

Parameters

interface UseOtpTimerProps {
  initialTimer: number;
  onTimeout: () => void;
  onTick?: (currentTime: number) => void;
  debug?: boolean;
}

Returns

interface UseOtpTimerReturn {
  timer: number; // Current timer value in seconds
  resetTimer: () => void; // Reset timer to initial value
  isActive: boolean; // Whether timer is currently running
  isExpired: boolean; // Whether timer has reached zero
  pauseTimer: () => void; // Pause the timer
  resumeTimer: () => void; // Resume paused timer
}

Performance Best Practices

1. Memoize Callbacks

Always use useCallback for your event handlers:

const handleResend = useCallback(async (count: number) => {
  // Your resend logic
}, []);

const handleTimeout = useCallback(() => {
  // Your timeout logic
}, []);

2. Stable Props

Avoid creating objects in render:

// ❌ Bad - creates new object on every render
<OtpTimer
  containerStyle={{ padding: 16 }}
  onResend={(count) => api.resend(count)}
/>

// ✅ Good - stable references
const containerStyle = { padding: 16 };
const handleResend = useCallback((count) => api.resend(count), []);

<OtpTimer
  containerStyle={containerStyle}
  onResend={handleResend}
/>

3. Component Memoization

The OtpTimer component is already memoized, but ensure parent components don't cause unnecessary re-renders:

const ParentComponent = React.memo(() => {
  // Component implementation
});

Background Behavior

The timer automatically handles app state changes:

  • Background: Records timestamp when app goes to background
  • Foreground: Calculates time spent in background and updates timer accordingly
  • Accuracy: Maintains precise timing regardless of background duration

Error Handling

The component provides several ways to handle errors:

const handleResend = async (count: number) => {
  try {
    await api.resendOtp();
    // Success - timer will reset automatically
  } catch (error) {
    showError('Failed to send OTP');
    throw error; // Re-throw to prevent timer reset
  }
};

Testing

Debug Mode

Enable debug mode for development:

<OtpTimer
  debug={__DEV__}
  // ... other props
/>

Testing Configuration

Use shorter timers and higher limits for testing:

const TIMER_CONFIG = __DEV__
  ? { initialTimer: 10, resendLimit: 10 }
  : { initialTimer: 300, resendLimit: 3 };

<OtpTimer
  {...TIMER_CONFIG}
  // ... other props
/>

Migration Guide

From Basic Timer Libraries

If you're migrating from a basic countdown timer:

  1. Replace timer prop with initialTimer
  2. Add required onResend callback
  3. Update styling props (most are compatible)
  4. Add error handling to resend function

Performance Considerations

  • Callbacks are automatically memoized internally
  • Component uses React.memo for render optimization
  • Background timing prevents unnecessary re-renders
  • All intervals and listeners are properly cleaned up

Examples

Check out the examples directory for complete implementation examples including:

  • Basic usage
  • Custom styling
  • Redux integration
  • Error handling
  • Testing setups

Contributing

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

License

MIT License - see the LICENSE file for details.

Made with ❤️ for the React Native community

Author

👤 Hamza Gulraiz

📬 Support me