Skip to content
design
Components

Hour meter input

An engine's or a generator's hour meter reading, typed as the meter shows it, with the hours run since the last reading and a question when it cannot be right.

On this page
hr
That's 90.0 hr since the last reading on 10 October 2026, but only 24.0 hr have passed. Check the figures.
An engine log's hour meter, read yesterday at 1234.5 hr, with a reading that gains more hours than have passed questioned.
Code
import { HourMeterInput } from "@bosunkit/components/hour-meter-input";
import { useState, type Dispatch, type ReactElement, type SetStateAction } from "react";

export const description: string =
  "An engine log's hour meter, read yesterday at 1234.5 hr: today's reading says the hours run since, and one that " +
  "gains more hours than have passed is questioned.";

export default function Showcase(): ReactElement {
  const [hours, setHours]: [number | null, Dispatch<SetStateAction<number | null>>] = useState<number | null>(1324.5);
  return (
    <div style={{ width: "100%", maxWidth: 420 }}>
      <HourMeterInput
        label="Main engine hours"
        previous={{ hours: 1234.5, at: "2026-10-10T12:00:00.000Z" }}
        now={Date.parse("2026-10-11T12:00:00.000Z")}
        value={hours}
        onValueChange={setHours}
      />
    </div>
  );
}

An hour meter input holds a reading of an engine's or a generator's hour meter, the figure maintenance is due by. It reads the reading as the meter shows it: decimal hours with a point or a comma, 1234.5, or hours and minutes as a meter with a clock face shows them, 1234:30, with or without an hours symbol after it. It holds the reading to the tenth of an hour a meter's last figure shows, and writes it so, with hr after it.

Given the reading before it, previous, the field writes the hours run since under it: "5.5 hr since the last reading on 10 October 2026". It questions a reading that cannot be right in an attention message. A reading below the last one is questioned, since a meter does not run backwards. So is one that gains more hours than have passed since the last reading's time. The reading is kept in both cases, because a new or reset meter starts again from zero, and the crew may know why. Minutes past 59, or text the field cannot read, keep their text and name the problem, and the field reports no reading until it is fixed, so a form never saves the one that stood before.

Examples

Empty

hr
As the meter shows it, such as 812.4 or 812:25
Empty, with no reading before it: the hint says what to write.
Code
import { HourMeterInput } from "@bosunkit/components/hour-meter-input";
import type { ReactElement } from "react";

export const description: string = "Empty, with no reading before it: the hint says what to write.";

export default function Empty(): ReactElement {
  return <HourMeterInput label="Generator hours" hint="As the meter shows it, such as 812.4 or 812:25" />;
}

Hours since the last reading

hr
5.5 hr since the last reading on 10 October 2026
With the reading before it: the hours run since that reading, under the field.
Code
import { HourMeterInput } from "@bosunkit/components/hour-meter-input";
import type { ReactElement } from "react";

export const description: string = "With the reading before it: the hours run since that reading, under the field.";

export default function Since(): ReactElement {
  return (
    <HourMeterInput
      label="Main engine hours"
      previous={{ hours: 1234.5, at: "2026-10-10T12:00:00.000Z" }}
      now={Date.parse("2026-10-11T12:00:00.000Z")}
      defaultValue={1240}
    />
  );
}

Backwards

hr
The last reading was 1234.5 hr on 10 October 2026, and a meter doesn't run backwards. Check the figures, or note a replaced meter.
A reading below the last one is questioned, since a meter does not run backwards, but kept: a new meter starts again from zero.
Code
import { HourMeterInput } from "@bosunkit/components/hour-meter-input";
import type { ReactElement } from "react";

export const description: string =
  "A reading below the last one is questioned, since a meter does not run backwards, but kept: a new meter starts " +
  "again from zero.";

export default function Backwards(): ReactElement {
  return (
    <HourMeterInput
      label="Main engine hours"
      previous={{ hours: 1234.5, at: "2026-10-10T12:00:00.000Z" }}
      now={Date.parse("2026-10-11T12:00:00.000Z")}
      defaultValue={1224.5}
    />
  );
}

Invalid

hr
Minutes past 59 or text the field cannot read keep their text and say why once the crew leave the field.
Code
import { HourMeterInput } from "@bosunkit/components/hour-meter-input";
import type { ReactElement } from "react";

export const description: string =
  "Minutes past 59 or text the field cannot read keep their text and say why once the crew leave the field.";

export default function Invalid(): ReactElement {
  return (
    <HourMeterInput
      label="Main engine hours"
      defaultValue={1234.5}
      error="Minutes run from 0 to 59. Check the minutes."
    />
  );
}

Props

PropTypeDefaultDescription
labelstring | undefined–The field's name on the outline, translated by the caller; omit it inside a FormField.
valuenumber | null | undefined–The reading in hours, to the tenth, or null for none.
defaultValuenumber | null | undefined–The reading it starts with when the caller does not hold value.
onValueChange((hours: number | null) => void) | undefined–Called with the reading when the crew type one and leave the field; null when they clear it.
previousHourMeterReading | undefined–The reading before this one, for the hours run since and the checks against it: a reading below it, or more hours since it than have passed, is questioned.
nownumber | undefined–The current time in epoch milliseconds, in place of the clock's, for the hours passed since the last reading.
hintstring | undefined–Text under the field while there is no reading before this one, translated by the caller.
errorstring | undefined–The caller's own error, shown in place of the field's.
requiredboolean | undefined–Whether the field must hold a reading.
disabledboolean | undefined–Whether the field takes no input.
readOnlyboolean | undefined–Whether the field shows its reading without letting the crew change it.
idstring | undefined–The input's id, for a Label's htmlFor.
namestring | undefined–The form field name; the form receives the hours.
aria-labelstring | undefined–Names the input when there is no label.
classNamestring | undefined–Classes added to the field's root.
testIdstring | undefined–data-testid on the field.
refRef<HTMLInputElement> | undefined–The input element.
motionMotionProp | undefined–How the error line moves; off by default, as FormField's.