Sign In

ink-timer

Package Overview
Dependencies
Maintainers
1
Versions
4
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

ink-timer

Timer, countdown, and stopwatch hooks and components for Ink

latest
Source
npmnpm
Version
0.2.2
Version published
Weekly downloads
4
-73.33%
Maintainers
1
Weekly downloads
 
Created
Source

ink-timer

npm version CI license npm downloads

ink-timer demo

Timer, countdown, and stopwatch hooks and components for Ink.

Features

  • Timer counts up from zero with start/stop/reset controls
  • Countdown counts down from a given duration and fires a callback on completion
  • Stopwatch counts up with lap recording and lap history
  • Four built-in format presets plus custom format functions
  • Keyboard controls out of the box (pause, resume, reset, lap)
  • Designed for React 19+ and Ink 6+
  • Full TypeScript types exported

Install

npm install ink-timer

Peer dependencies: react (>=19) and ink (>=6). The components (<Timer>, <Countdown>, <Stopwatch>) render with Ink, so they require Ink 6+ (which in turn requires React 19+). The hooks (useTimer, useCountdown, useStopwatch) and their formatTime output are ink-free, so you can use them with any React 19+ renderer.

Quick Start

Timer

import { render } from 'ink';
import { Timer } from 'ink-timer';

render(<Timer prefix="Elapsed: " format="human" enableKeyboard />);
// Elapsed: 0s
// Elapsed: 1s
// Elapsed: 2s ...

Countdown

import { render } from 'ink';
import { Countdown } from 'ink-timer';

render(
  <Countdown
    duration={30_000}
    prefix="Remaining: "
    color="yellow"
    onComplete={() => console.log('Done!')}
  />
);
// Remaining: 00:30
// Remaining: 00:29 ...

Stopwatch

import { render } from 'ink';
import { Stopwatch } from 'ink-timer';

render(
  <Stopwatch enableKeyboard showLaps maxLapsDisplay={5} color="cyan" />
);
// 00:00
// (press L to record laps)

Hooks API

useTimer(options?)

A hook that counts elapsed time upward.

Options

OptionTypeDefaultDescription
autoStartbooleantrueStart the timer immediately on mount
intervalnumber1000Update interval in milliseconds
onTick(elapsedMs: number) => void-Callback fired on every interval tick while running
formatFormatOption"digital"Format preset name or custom format function

Returns UseTimerResult

PropertyTypeDescription
elapsedMsnumberElapsed time in milliseconds since start (excluding paused periods)
isRunningbooleanWhether the timer is currently running
formattedFormattedTimeFormatted time breakdown (see below)
start() => voidStart or resume the timer. No-op if already running
stop() => voidPause the timer. No-op if not running
reset() => voidReset to 0 and stop
toggle() => voidToggle between running and stopped

useCountdown(options)

A hook that counts down from a given duration.

Options

OptionTypeDefaultDescription
durationnumberrequiredTotal countdown duration in milliseconds
autoStartbooleantrueStart counting down immediately on mount
intervalnumber1000Update interval in milliseconds
onTick(remainingMs: number) => void-Callback fired on every interval tick while running
onComplete() => void-Callback fired exactly once when the countdown reaches 0
formatFormatOption"digital"Format preset name or custom format function

Returns UseCountdownResult

PropertyTypeDescription
remainingMsnumberRemaining time in milliseconds (clamped to >= 0)
isRunningbooleanWhether the countdown is currently running
isCompletebooleanWhether the countdown has reached 0
formattedFormattedTimeFormatted time breakdown of remaining time
start() => voidStart or resume. No-op if already running or complete
stop() => voidPause. No-op if not running
reset() => voidReset to the original duration and stop
toggle() => voidToggle between running and stopped
restart() => voidReset and immediately start

useStopwatch(options?)

A hook that counts elapsed time upward with lap recording.

Options

OptionTypeDefaultDescription
autoStartbooleantrueStart the stopwatch immediately on mount
intervalnumber1000Update interval in milliseconds
onTick(elapsedMs: number) => void-Callback fired on every interval tick while running
onLap(lap: Lap) => void-Callback fired when a new lap is recorded
formatFormatOption"digital"Format preset name or custom format function

Returns UseStopwatchResult

PropertyTypeDescription
elapsedMsnumberTotal elapsed time in milliseconds (excluding paused periods)
isRunningbooleanWhether the stopwatch is currently running
lapsreadonly Lap[]Array of recorded laps in chronological order
formattedFormattedTimeFormatted time breakdown
start() => voidStart or resume. No-op if already running
stop() => voidPause. No-op if not running
reset() => voidReset to 0, clear all laps, and stop
toggle() => voidToggle between running and stopped
lap() => voidRecord a lap. Only works while running

Lap

PropertyTypeDescription
numbernumber1-indexed lap number
durationMsnumberDuration of this individual lap in milliseconds
cumulativeMsnumberCumulative elapsed time at the moment this lap was recorded
formattedFormattedTimeFormatted breakdown of this lap's duration

Note: Each lap's formatted field is a snapshot created at recording time using the format option that was active when the lap was recorded. If you change the format option after recording laps, previously recorded laps retain their original formatting.

FormattedTime

Returned by the formatted field of every hook.

PropertyTypeDescription
textstringPre-formatted display string based on the active format option
hoursnumberWhole hours component
minutesnumberWhole minutes component (0-59)
secondsnumberWhole seconds component (0-59)
millisecondsnumberRemaining milliseconds component (0-999)
totalMsnumberTotal elapsed or remaining time in milliseconds

Components API

<Timer>

Renders a timer display that counts up.

PropTypeDefaultDescription
autoStartbooleantrueStart automatically on mount
intervalnumber1000Update interval in milliseconds
formatFormatOption"digital"Format preset or custom format function
showMillisecondsbooleanfalseShorthand to switch to "digital-ms" format preset
prefixstring-Text to render before the time string
suffixstring-Text to render after the time string
colorstring-Text color (any value supported by Ink's <Text color>)
boldbooleanfalseRender time text in bold
dimWhenPausedbooleantrueDim the time text when paused
enableKeyboardbooleanfalseEnable keyboard controls
onTick(elapsedMs: number) => void-Callback fired every tick

<Countdown>

Renders a countdown display.

PropTypeDefaultDescription
durationnumberrequiredTotal countdown duration in milliseconds
autoStartbooleantrueStart automatically on mount
intervalnumber1000Update interval in milliseconds
formatFormatOption"digital"Format preset or custom format function
showMillisecondsbooleanfalseShorthand to switch to "digital-ms" format preset
prefixstring-Text to render before the time string
suffixstring-Text to render after the time string
colorstring-Text color
boldbooleanfalseRender time text in bold
dimWhenPausedbooleantrueDim the time text when paused
enableKeyboardbooleanfalseEnable keyboard controls
onTick(remainingMs: number) => void-Callback fired every tick
onComplete() => void-Callback fired when countdown reaches 0

<Stopwatch>

Renders a stopwatch display with optional lap list.

PropTypeDefaultDescription
autoStartbooleantrueStart automatically on mount
intervalnumber1000Update interval in milliseconds
formatFormatOption"digital"Format preset or custom format function
showMillisecondsbooleanfalseShorthand to switch to "digital-ms" format preset
prefixstring-Text to render before the time string
suffixstring-Text to render after the time string
colorstring-Text color
boldbooleanfalseRender time text in bold
dimWhenPausedbooleantrueDim the time text when paused
enableKeyboardbooleanfalseEnable keyboard controls
onTick(elapsedMs: number) => void-Callback fired every tick
onLap(lap: Lap) => void-Callback fired when a lap is recorded
showLapsbooleantrueShow lap list below the elapsed time
maxLapsDisplaynumber0Maximum laps to display (most recent first). 0 = unlimited
enableLapKeybooleantrueEnable the L key for recording laps (only when enableKeyboard is true)

Format Options

Built-in Presets

"digital" (default)

Minutes and seconds, with hours added automatically when needed.

0s     -> 00:00
5s     -> 00:05
2m 30s -> 02:30
1h 1m  -> 1:01:01

"digital-ms"

Same as "digital" but with milliseconds appended.

0s          -> 00:00.000
2m 30.45s   -> 02:30.450
1h 1m 1.12s -> 1:01:01.123

"human"

Human-readable short units.

0s     -> 0s
5s     -> 5s
2m 30s -> 2m 30s
1h 1m  -> 1h 1m 1s

"human-ms"

Same as "human" but with milliseconds appended.

0s          -> 0s 0ms
2m 30.45s   -> 2m 30s 450ms
1h 1m 1.12s -> 1h 1m 1s 123ms

Custom Format Function

Pass a function that receives milliseconds and returns a string.

<Timer
  format={(ms) => `${Math.floor(ms / 1000)} seconds`}
/>
// 0 seconds
// 1 seconds
// 2 seconds ...

The formatted object still provides numeric hours, minutes, seconds, and milliseconds components regardless of the format function.

Keyboard Controls

When enableKeyboard is set to true, the following keys are active:

KeyActionComponents
SpacePause / resumeTimer, Countdown, Stopwatch
R / rResetTimer, Countdown, Stopwatch
L / lRecord a lapStopwatch (when enableLapKey is true)

Advanced Examples

Pause and Resume

import { useTimer } from 'ink-timer';
import { Text, Box } from 'ink';

function PauseableTimer() {
  const { formatted, isRunning, start, stop } = useTimer({ autoStart: false });

  return (
    <Box flexDirection="column">
      <Text>{formatted.text}</Text>
      <Text dimColor>
        {isRunning ? 'Running (call stop() to pause)' : 'Paused (call start() to resume)'}
      </Text>
    </Box>
  );
}

Custom Formatting

import { useCountdown } from 'ink-timer';
import { Text } from 'ink';

function FriendlyCountdown() {
  const { formatted, isComplete } = useCountdown({
    duration: 60_000,
    format: (ms) => {
      const s = Math.ceil(ms / 1000);
      if (s > 10) return `${s} seconds left`;
      if (s > 0) return `${s}...`;
      return 'Go!';
    },
  });

  return <Text color={isComplete ? 'green' : 'yellow'}>{formatted.text}</Text>;
}

Multiple Timers

import { Timer, Countdown } from 'ink-timer';
import { Box, Text } from 'ink';

function Dashboard() {
  return (
    <Box flexDirection="column" gap={1}>
      <Box>
        <Text>Uptime: </Text>
        <Timer format="human" color="green" />
      </Box>
      <Box>
        <Text>Break ends in: </Text>
        <Countdown
          duration={5 * 60_000}
          format="digital"
          color="yellow"
          onComplete={() => console.log('Break over!')}
        />
      </Box>
    </Box>
  );
}

Lap Tracking

import { useStopwatch } from 'ink-timer';
import { Text, Box } from 'ink';

function LapTracker() {
  const { formatted, laps, lap, reset } = useStopwatch({
    format: 'digital-ms',
    interval: 100,
  });

  return (
    <Box flexDirection="column">
      <Text bold>{formatted.text}</Text>
      {laps.map((l) => (
        <Text key={l.number}>
          Lap {l.number}: {l.formatted.text} (total: {l.cumulativeMs}ms)
        </Text>
      ))}
    </Box>
  );
}

TypeScript

All types are exported from the package entry point:

import type {
  FormatFunction,
  FormatPreset,
  FormatOption,
  FormattedTime,
  UseTimerOptions,
  UseTimerResult,
  UseCountdownOptions,
  UseCountdownResult,
  UseStopwatchOptions,
  UseStopwatchResult,
  Lap,
  TimerProps,
  CountdownProps,
  StopwatchProps,
  TimerDisplayProps,
} from 'ink-timer';

Contributing

Contributions welcome! Please open an issue first to discuss what you'd like to change.

Changelog

See GitHub Releases.

License

MIT

Keywords

ink

FAQs

Package last updated on 25 Jul 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts