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 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-native-otp-timer-lite

v0.1.0

Published

A lightweight OTP resend countdown timer for React Native — background-accurate hook plus an optional drop-in component.

Readme

react-native-otp-timer-lite

A lightweight OTP resend countdown timer for React Native.

Ships a headless useOtpTimer hook for building your own UI, plus an optional <OtpTimer /> component when you just want something that works. No native modules — pure TypeScript, so it drops into Expo and bare React Native alike.

Background-accurate. The countdown is derived from a wall-clock deadline rather than from a count of setInterval ticks. When the OS throttles or suspends JS timers — which it does as soon as your app is backgrounded — the remaining time is still correct the moment the user returns. The hook also re-syncs on AppState change, so the UI snaps to the right value immediately on resume instead of drifting for a second first.

Installation

npm install react-native-otp-timer-lite
yarn add react-native-otp-timer-lite

react and react-native are peer dependencies — no extra linking, pods, or config required.

Usage

The hook (build your own UI)

TypeScript

import React from 'react';
import { Button, Text, View } from 'react-native';
import { useOtpTimer } from 'react-native-otp-timer-lite';

export function VerifyScreen() {
  const { formatted, isRunning, start } = useOtpTimer(30);

  const handleResend = () => {
    sendOtp();
    start(); // restart the countdown
  };

  return (
    <View>
      {isRunning ? (
        <Text>Resend OTP in {formatted}</Text>
      ) : (
        <Button title="Resend OTP" onPress={handleResend} />
      )}
    </View>
  );
}

JavaScript

import React from 'react';
import { Button, Text, View } from 'react-native';
import { useOtpTimer } from 'react-native-otp-timer-lite';

export function VerifyScreen() {
  const { formatted, isRunning, start } = useOtpTimer(30);

  const handleResend = () => {
    sendOtp();
    start();
  };

  return (
    <View>
      {isRunning ? (
        <Text>Resend OTP in {formatted}</Text>
      ) : (
        <Button title="Resend OTP" onPress={handleResend} />
      )}
    </View>
  );
}

The component (drop-in)

TypeScript

import React from 'react';
import { OtpTimer } from 'react-native-otp-timer-lite';

export function VerifyScreen() {
  return (
    <OtpTimer
      seconds={30}
      onResend={() => sendOtp()}
      textStyle={{ color: '#888' }}
    />
  );
}

JavaScript

import React from 'react';
import { OtpTimer } from 'react-native-otp-timer-lite';

export function VerifyScreen() {
  return <OtpTimer seconds={30} onResend={() => sendOtp()} />;
}

By default the component renders Resend OTP in 00:30 while counting down, then swaps to a tappable Resend OTP label at zero — which fires onResend and restarts the countdown.

Custom rendering

renderContent hands you the timer state and takes over rendering entirely, so you keep the timing logic without the default markup:

<OtpTimer
  seconds={60}
  onResend={sendOtp}
  renderContent={({ formatted, isRunning, resend }) => (
    <Pressable onPress={resend} disabled={isRunning}>
      <Text>{isRunning ? `Wait ${formatted}` : 'Send again'}</Text>
    </Pressable>
  )}
/>

Not starting immediately

Pass autoStart: false when the countdown should begin only after the OTP is actually sent:

const { formatted, isRunning, start } = useOtpTimer(30, { autoStart: false });

const sendCode = async () => {
  await api.sendOtp();
  start();
};

API reference

useOtpTimer(seconds, options?)

Parameters

| Parameter | Type | Default | Description | | --------- | -------------------- | ---------- | ------------------------------------ | | seconds | number | required | Countdown duration in seconds. | | options | UseOtpTimerOptions | {} | Optional configuration (see below). |

options

| Option | Type | Default | Description | | ----------- | ------------ | ------- | ------------------------------------------------------------ | | autoStart | boolean | true | Start counting down as soon as the hook mounts. | | onExpire | () => void | — | Called once each time the countdown reaches zero. | | interval | number | 1000 | How often the displayed value refreshes, in milliseconds. |

Returns

| Property | Type | Description | | ----------- | ----------------------------- | ---------------------------------------------------------------------------------- | | timeLeft | number | Whole seconds remaining. Never negative. | | isRunning | boolean | true while the countdown is active; false once it hits zero or is reset. | | formatted | string | timeLeft rendered as MM:SS — e.g. "00:30", "02:05". | | start | (seconds?: number) => void | Begin or restart the countdown. Pass a value to override the duration for this run. | | reset | (seconds?: number) => void | Stop the countdown and restore the full duration without starting it. |

<OtpTimer />

| Prop | Type | Default | Description | | ----------------- | ------------------------------------------ | ------------------------ | --------------------------------------------------------------------------- | | seconds | number | 30 | Countdown duration in seconds. | | onResend | () => void | — | Fired when the user taps resend after the countdown finishes. | | autoStart | boolean | true | Begin counting down on mount. | | restartOnResend | boolean | true | Restart the countdown automatically after a resend. | | textTemplate | string | 'Resend OTP in {time}' | Text shown while counting down. {time} is replaced with the MM:SS value. | | resendLabel | string | 'Resend OTP' | Label for the resend control. | | onExpire | () => void | — | Called once each time the countdown reaches zero. | | containerStyle | StyleProp<ViewStyle> | — | Style applied to the wrapping View. | | textStyle | StyleProp<TextStyle> | — | Style applied to the countdown text. | | resendTextStyle | StyleProp<TextStyle> | — | Style applied to the resend label. | | renderContent | (state) => React.ReactElement \| null | — | Overrides the default rendering entirely. Receives { timeLeft, formatted, isRunning, resend }. | | testID | string | — | Applied to the container; the resend control gets `${testID}-resend`. |

Notes

  • onResend is only reachable once the countdown has finished — the resend control is not rendered while the timer is running, so users cannot spam it.
  • reset() stops the timer and restores the full duration; start() is what actually begins counting. Call start() if you want reset-and-run.
  • onExpire fires exactly once per run, even if the app was backgrounded across the moment the timer hit zero.

License

MIT © Melby Thomas