Skip to Content
Hashti Design System
Components

Number Field

Numeric input field featuring stepper decrement/increment controls, boundary constraints, and currency/percentage formatting.

Number Field

The NumberField component enables users to enter, increment, and decrement numeric values. It pairs a styled numeric text field with integrated tactile stepper buttons (+ and -), supporting fine-grained constraints, step increments, and internationalized currency and percentage formatting.

Like all Hashti components, NumberField reflects deliberate geometric proportions and provides full accessibility according to the W3C WAI-ARIA spinbutton pattern.

NumberFieldErgonomic numerical input with step buttons and unit formatting.
Standard ergonomic touch boundary in pixels.

Import

import { NumberField } from "@hashti/react";

Compositional sub-components are also available:

import { NumberField, FieldControl, FieldGroupInput, FieldButton } from "@hashti/react";

Examples

Standard Number Field

A standard NumberField includes an accessible label, an input area, and increment/decrement buttons.

import { NumberField } from "@hashti/react";

export function StandardNumberFieldDemo() {
  return (
    <NumberField
      label="Guest Count"
      defaultValue={2}
      description="Select the total number of overnight visitors."
    />
  );
}

Boundary Limits (Min & Max)

Use minValue and maxValue to enforce safe bounds. Stepper buttons automatically disable when limits are reached, and out-of-range typing triggers validation feedback.

import { NumberField } from "@hashti/react";

export function MinMaxDemo() {
  return (
    <NumberField
      label="Room Occupancy"
      defaultValue={1}
      minValue={1}
      maxValue={8}
      description="Minimum 1 guest, maximum 8 guests."
    />
  );
}

Custom Step Intervals

The step prop defines the increment and decrement amount. By default, step is 1. For decimal or large-interval measurements, pass fractions or custom integers.

import { NumberField } from "@hashti/react";

export function StepDemo() {
  return (
    <div className="flex flex-col gap-4 max-w-sm">
      {/* Fractional increments */}
      <NumberField
        label="Arch Thickness (meters)"
        defaultValue={0.5}
        step={0.25}
        minValue={0.25}
        maxValue={5.0}
      />

      {/* Large increments */}
      <NumberField
        label="Square Meters"
        defaultValue={150}
        step={10}
        minValue={50}
        maxValue={2000}
      />
    </div>
  );
}

Internationalized Currency Formatting

Using the formatOptions property (powered by JavaScript’s Intl.NumberFormat), you can format inputs in any currency while preserving numeric parsing under the hood.

import { NumberField } from "@hashti/react";

export function CurrencyDemo() {
  return (
    <div className="flex flex-col gap-4 max-w-sm">
      {/* US Dollar */}
      <NumberField
        label="Nightly Rate (USD)"
        defaultValue={120}
        formatOptions={{
          style: "currency",
          currency: "USD",
          currencyDisplay: "symbol",
        }}
      />

      {/* Euro */}
      <NumberField
        label="Service Deposit (EUR)"
        defaultValue={50}
        formatOptions={{
          style: "currency",
          currency: "EUR",
          currencyDisplay: "symbol",
        }}
      />
    </div>
  );
}

Percentage Formatting

Format ratios or commission percentages with automatic multiplication by 100 and localized percent symbols:

import { NumberField } from "@hashti/react";

export function PercentageDemo() {
  return (
    <NumberField
      label="Preservation Discount"
      defaultValue={0.15}
      step={0.01}
      minValue={0}
      maxValue={1}
      formatOptions={{
        style: "percent",
        minimumFractionDigits: 0,
        maximumFractionDigits: 2,
      }}
    />
  );
}

Sizes & Disabled State

NumberField supports Hashti’s standard control sizes (sm, md, lg) and full disablement:

import { NumberField } from "@hashti/react";

export function StatesDemo() {
  return (
    <div className="flex flex-col gap-4 max-w-sm">
      <NumberField size="sm" label="Small (sm)" defaultValue={10} />
      <NumberField size="md" label="Medium (md)" defaultValue={25} />
      <NumberField size="lg" label="Large (lg)" defaultValue={50} />
      <NumberField
        label="Locked Allocation"
        defaultValue={100}
        isDisabled
        description="Quota allocation is managed by project owners."
      />
    </div>
  );
}

Props Reference

PropTypeDefaultDescription
valuenumber—The controlled numerical value.
defaultValuenumber—The default numerical value for uncontrolled usage.
onChange(value: number) => void—Callback invoked with the parsed numeric value upon change.
minValuenumber—The minimum permissible value.
maxValuenumber—The maximum permissible value.
stepnumber1The amount to increment or decrement per step.
formatOptionsIntl.NumberFormatOptions—Formatting options for currency, percentage, decimals, or units.
size'sm' | 'md' | 'lg''md'Height and control sizing of the input and buttons.
labelReactNode—Accessible field label.
descriptionReactNode—Explanatory helper text beneath or above the control.
errorMessageReactNode—Error text rendered when in an invalid state.
isInvalidbooleanfalseWhether the number field currently violates validation rules.
isRequiredbooleanfalseMarks the number field as required for submission.
isDisabledbooleanfalsePrevents user interaction with the input and stepper controls.
isReadOnlybooleanfalseAllows viewing and copying the value while preventing changes.

Accessibility

NumberField implements the WAI-ARIA spinbutton role pattern:

  • ArrowUp & ArrowDown: Pressing ↑ increments the value by step; pressing ↓ decrements the value by step.
  • PageUp & PageDown: Pressing PageUp increments the value by a larger step (typically 10x the primary step); PageDown decrements by the larger step.
  • Home & End: Pressing Home immediately jumps to minValue (if defined); pressing End jumps to maxValue (if defined).
  • Accessible Stepper Buttons: Decrement and increment buttons feature clear localized labels (aria-label="Decrement", aria-label="Increment") and are marked tabIndex={-1} so keyboard users navigate smoothly through form fields without extra tab stops.
  • Screen Reader Announcements: The component sets aria-valuenow, aria-valuemin, aria-valuemax, and aria-valuetext (reflecting internationalized currency and percentage strings) so screen readers accurately speak the formatted value.

Keyboard Interactions

KeyAction
↑ / ↓Increments or decrements the numeric value by step.
Page Up / Page DownIncrements or decrements value by larger page step.
Home / EndJumps directly to minimum or maximum permitted value.
TabMoves focus into or out of the number input control.

WAI-ARIA & Data Attributes

AttributeTargetDescription
[data-hovered]Stepper, InputPresent when hovered by pointer.
[data-focused]Stepper, InputActive when receiving focus.
[data-disabled]Stepper, InputSet when interaction is disabled.