27.7k

NumberField

Number input fields with increment/decrement buttons, validation, and internationalized formatting

Import

import { NumberField } from '@darkcode-ui/react';

Usage

import {Label, NumberField} from "@darkcode-ui/react";

export function Basic() {
  return (
    <NumberField className="w-full max-w-64" defaultValue={1024} minValue={0} name="width">

Anatomy

import {NumberField, Label, Description, FieldError} from '@darkcode-ui/react';

export default () => (
  <NumberField>
    <Label />
    <NumberField.Group>
      <NumberField.DecrementButton />
      <NumberField.Input />
      <NumberField.IncrementButton />
    </NumberField.Group>
    <Description />
    <FieldError />
  </NumberField>
)

NumberField allows users to enter numeric values with optional increment/decrement buttons. It supports internationalized formatting, validation, and keyboard navigation.

With Description

import {Description, Label, NumberField} from "@darkcode-ui/react";

export function WithDescription() {
  return (
    <div className="flex w-full max-w-64 flex-col gap-4">

Required Field

import {Description, Label, NumberField} from "@darkcode-ui/react";

export function Required() {
  return (
    <div className="flex w-full max-w-64 flex-col gap-4">

Validation

Use isInvalid together with FieldError to surface validation messages.

import {FieldError, Label, NumberField} from "@darkcode-ui/react";

export function Validation() {
  return (
    <div className="flex w-full max-w-64 flex-col gap-4">

Controlled

Control the value to synchronize with other components or perform custom formatting.

"use client";

import {Button, Description, Label, NumberField} from "@darkcode-ui/react";
import React from "react";

With Validation

Implement custom validation logic with controlled values.

"use client";

import {Description, FieldError, Label, NumberField} from "@darkcode-ui/react";
import React from "react";

Step Values

Configure increment/decrement step values for precise control.

import {Description, Label, NumberField} from "@darkcode-ui/react";

export function WithStep() {
  return (
    <div className="flex w-full max-w-64 flex-col gap-4">

Format Options

Format numbers as currency, percentages, decimals, or units with internationalization support.

import {Description, Label, NumberField} from "@darkcode-ui/react";

export function WithFormatOptions() {
  return (
    <div className="flex w-full max-w-64 flex-col gap-4">

Custom Icons

Customize the increment and decrement button icons.

import {Description, Label, NumberField} from "@darkcode-ui/react";

export function CustomIcons() {
  return (
    <div className="flex w-full max-w-64 flex-col gap-4">

With Chevrons

Use chevron icons in a vertical layout for a different visual style.

import {Label, NumberField} from "@darkcode-ui/react";

export function WithChevrons() {
  return (
    <NumberField

Disabled State

import {Description, Label, NumberField} from "@darkcode-ui/react";

export function Disabled() {
  return (
    <div className="flex w-full max-w-64 flex-col gap-4">

Full Width

import {Label, NumberField} from "@darkcode-ui/react";

export function FullWidth() {
  return (
    <div className="w-[400px] space-y-4">

Variants

The NumberField component supports two visual variants:

  • primary (default) - Standard styling with shadow, suitable for most use cases
  • secondary - Lower emphasis variant without shadow, suitable for use in Surface components
import {Label, NumberField} from "@darkcode-ui/react";

export function Variants() {
  return (
    <div className="flex flex-col gap-4">

In Surface

When used inside a Surface component, use variant="secondary" to apply the lower emphasis variant suitable for surface backgrounds.

import {Description, Label, NumberField, Surface} from "@darkcode-ui/react";

export function OnSurface() {
  return (
    <Surface className="flex w-full max-w-[280px] flex-col gap-4 rounded-3xl p-6">

Form Example

Complete form integration with validation and submission handling.

"use client";

import {
  Button,
  Description,

Custom Render Function

"use client";

import {Label, NumberField} from "@darkcode-ui/react";

export function CustomRenderFunction() {

Number Stepper

The stepper pattern swaps the editable input for an animated value display — NumberField.Value, powered by Number Flow. Compose DecrementButton → Value → IncrementButton inside the group. The group automatically renders a visually-hidden input, so the field stays a focusable, screen-reader-announced spinbutton while the value animates as it changes.

import {NumberField} from '@darkcode-ui/react';

export default () => (
  <NumberField aria-label="Quantity" defaultValue={1} minValue={0}>
    <NumberField.Group>
      <NumberField.DecrementButton />
      <NumberField.Value />
      <NumberField.IncrementButton />
    </NumberField.Group>
  </NumberField>
);
import {NumberField} from "@darkcode-ui/react";

export function Stepper() {
  return (
    <NumberField aria-label="Quantity" defaultValue={1} minValue={0} name="quantity">

Guest Picker

import {NumberField} from "@darkcode-ui/react";

export function StepperGuestPicker() {
  return (
    <div className="flex w-72 items-center justify-between gap-6">

Stepper — Controlled

"use client";

import {Button, NumberField} from "@darkcode-ui/react";
import React from "react";

Stepper — Custom Icons

import {NumberField} from "@darkcode-ui/react";

export function StepperCustomIcons() {
  return (
    <NumberField aria-label="Volume" defaultValue={5} minValue={0} name="volume">

Custom Value

Pass a render function to NumberField.Value to fully customize the rendered content.

"use client";

import {NumberField} from "@darkcode-ui/react";

export function StepperCustomValue() {

Stepper — Disabled

import {NumberField} from "@darkcode-ui/react";

export function StepperDisabled() {
  return (
    <NumberField isDisabled aria-label="Quantity" defaultValue={4} minValue={0} name="quantity">

Min / Max Values

The increment and decrement buttons disable automatically at the bounds.

import {NumberField} from "@darkcode-ui/react";

export function StepperMinMax() {
  return (
    <div className="flex flex-col items-center gap-2">

Reversed Layout

import {NumberField} from "@darkcode-ui/react";

export function StepperReversedLayout() {
  return (
    <NumberField aria-label="Quantity" defaultValue={2} minValue={0} name="quantity">

Sizes

Use the size prop (sm, md, lg) on the root.

import {NumberField} from "@darkcode-ui/react";

const sizes = ["sm", "md", "lg"] as const;

export function StepperSizes() {

With Custom Buttons

import {NumberField} from "@darkcode-ui/react";

export function StepperCustomButtons() {
  return (
    <NumberField aria-label="Quantity" defaultValue={2} minValue={0} name="quantity">

Stepper — Format Options

import {NumberField} from "@darkcode-ui/react";

export function StepperFormatOptions() {
  return (
    <div className="flex flex-col gap-6">

With Label

import {Description, Label, NumberField} from "@darkcode-ui/react";

export function StepperWithLabel() {
  return (
    <NumberField defaultValue={1} minValue={0} name="tickets">

Stepper — With Step

import {Label, NumberField} from "@darkcode-ui/react";

export function StepperWithStep() {
  return (
    <div className="flex flex-col gap-6">

Styling

Passing Tailwind CSS classes

import {NumberField, Label} from '@darkcode-ui/react';

function CustomNumberField() {
  return (
    <NumberField className="gap-2">
      <Label className="text-sm font-semibold">Quantity</Label>
      <NumberField.Group className="rounded-xl border-2">
        <NumberField.DecrementButton className="bg-gray-100" />
        <NumberField.Input className="text-center font-bold" />
        <NumberField.IncrementButton className="bg-gray-100" />
      </NumberField.Group>
    </NumberField>
  );
}

Customizing the component classes

NumberField uses CSS classes that can be customized. Override the component classes to match your design system.

@layer components {
  .number-field {
    @apply flex flex-col gap-1;
  }

  /* When invalid, the description is hidden automatically */
  .number-field[data-invalid="true"] [data-slot="description"],
  .number-field[aria-invalid="true"] [data-slot="description"] {
    @apply hidden;
  }

  .number-field__group {
    @apply bg-field text-field-foreground shadow-field rounded-field inline-flex h-9 items-center overflow-hidden border;
  }

  .number-field__input {
    @apply flex-1 rounded-none border-0 bg-transparent px-3 py-2 tabular-nums;
  }

  .number-field__increment-button,
  .number-field__decrement-button {
    @apply flex h-full w-10 items-center justify-center rounded-none bg-transparent;
  }
}

CSS Classes

  • .number-field – Root container with minimal styling (flex flex-col gap-1)
  • .number-field__group – Container for input and buttons with border and background styling
  • .number-field__input – The numeric input field
  • .number-field__increment-button – Button to increment the value
  • .number-field__decrement-button – Button to decrement the value
  • .number-field--primary – Primary variant with shadow (default)
  • .number-field--secondary – Secondary variant without shadow, suitable for use in surfaces

Note: Child components (Label, Description, FieldError) have their own CSS classes and styling. See their respective documentation for customization options.

Interactive States

NumberField automatically manages these data attributes based on its state:

  • Invalid: [data-invalid="true"] or [aria-invalid="true"] - Automatically hides the description slot when invalid
  • Disabled: [data-disabled="true"] - Applied when isDisabled is true
  • Focus Within: [data-focus-within="true"] - Applied when the input or buttons are focused
  • Focus Visible: [data-focus-visible="true"] - Applied when focus is visible (keyboard navigation)
  • Hovered: [data-hovered="true"] - Applied when hovering over buttons

Additional attributes are available through render props (see NumberFieldRenderProps below).

API Reference

NumberField Props

NumberField inherits all props from React Aria's NumberField component.

Base Props

PropTypeDefaultDescription
childrenReact.ReactNode | (values: NumberFieldRenderProps) => React.ReactNode-Child components (Label, Group, Input, etc.) or render function.
classNamestring | (values: NumberFieldRenderProps) => string-CSS classes for styling, supports render props.
styleReact.CSSProperties | (values: NumberFieldRenderProps) => React.CSSProperties-Inline styles, supports render props.
fullWidthbooleanfalseWhether the number field should take full width of its container
idstring-The element's unique identifier.
variant"primary" | "secondary""primary"Visual variant of the component. primary is the default style with shadow. secondary is a lower emphasis variant without shadow, suitable for use in surfaces.
size"sm" | "md" | "lg""md"Size variant. Affects the group height, the button column width, and the stepper value text size.
renderDOMRenderFunction<keyof React.JSX.IntrinsicElements, NumberFieldRenderProps>-Overrides the default DOM element with a custom render function.

Value Props

PropTypeDefaultDescription
valuenumber-Current value (controlled).
defaultValuenumber-Default value (uncontrolled).
onChange(value: number | undefined) => void-Handler called when the value changes.

Formatting Props

PropTypeDefaultDescription
formatOptionsIntl.NumberFormatOptions-Options for formatting numbers (currency, percent, decimal, unit).
localestring-Locale for number formatting.

Validation Props

PropTypeDefaultDescription
isRequiredbooleanfalseWhether user input is required before form submission.
isInvalidboolean-Whether the value is invalid.
validate(value: number) => ValidationError | true | null | undefined-Custom validation function.
validationBehavior'native' | 'aria''native'Whether to use native HTML form validation or ARIA attributes.
validationErrorsstring[]-Server-side validation errors.

Range Props

PropTypeDefaultDescription
minValuenumber-Minimum allowed value.
maxValuenumber-Maximum allowed value.
stepnumber1Step value for increment/decrement operations.

State Props

PropTypeDefaultDescription
isDisabledboolean-Whether the input is disabled.
isReadOnlyboolean-Whether the input can be selected but not changed.

Form Props

PropTypeDefaultDescription
namestring-Name of the input element, for HTML form submission.
autoFocusboolean-Whether the element should receive focus on render.

Accessibility Props

PropTypeDefaultDescription
aria-labelstring-Accessibility label when no visible label is present.
aria-labelledbystring-ID of elements that label this field.
aria-describedbystring-ID of elements that describe this field.
aria-detailsstring-ID of elements with additional details.

Composition Components

NumberField works with these separate components that should be imported and used directly:

  • NumberField.Group - Container for input and buttons
  • NumberField.Input - The numeric input field
  • NumberField.Value - Animated value display (Number Flow) for the stepper pattern
  • NumberField.IncrementButton - Button to increment the value
  • NumberField.DecrementButton - Button to decrement the value
  • Label - Field label component from @darkcode-ui/react
  • Description - Helper text component from @darkcode-ui/react
  • FieldError - Validation error message from @darkcode-ui/react

Each of these components has its own props API. Use them directly within NumberField for composition:

<NumberField isRequired isInvalid={hasError} minValue={0} maxValue={100}>
  <Label>Quantity</Label>
  <NumberField.Group>
    <NumberField.DecrementButton />
    <NumberField.Input />
    <NumberField.IncrementButton />
  </NumberField.Group>
  <Description>Enter a value between 0 and 100</Description>
  <FieldError>Value must be between 0 and 100</FieldError>
</NumberField>

NumberField.Group Props

NumberField.Group inherits props from React Aria's Group component.

PropTypeDefaultDescription
childrenReact.ReactNode | (values: GroupRenderProps) => React.ReactNode-Child components (Input, Buttons) or render function.
classNamestring | (values: GroupRenderProps) => string-CSS classes for styling.

NumberField.Input Props

NumberField.Input inherits props from React Aria's Input component.

PropTypeDefaultDescription
classNamestring-CSS classes for styling.
variant"primary" | "secondary""primary"Visual variant of the input. primary is the default style with shadow. secondary is a lower emphasis variant without shadow, suitable for use in surfaces.

NumberField.IncrementButton Props

NumberField.IncrementButton inherits props from React Aria's Button component.

PropTypeDefaultDescription
childrenReact.ReactNode<IconPlus />Icon or content for the button. Defaults to plus icon.
classNamestring-CSS classes for styling.
slot"increment""increment"Must be set to "increment" (automatically set).

NumberField.DecrementButton Props

NumberField.DecrementButton inherits props from React Aria's Button component.

PropTypeDefaultDescription
childrenReact.ReactNode<IconMinus />Icon or content for the button. Defaults to minus icon.
classNamestring-CSS classes for styling.
slot"decrement""decrement"Must be set to "decrement" (automatically set).

NumberField.Value Props

NumberField.Value renders the field's current value as an animated number via Number Flow. It also accepts all Number Flow props except value and children.

PropTypeDefaultDescription
valuenumberfield valueOverride the displayed value. Defaults to the field's current value.
formatIntl.NumberFormatOptionsroot formatOptionsOverride the format options used to render the number.
childrenReact.ReactNode | ((args: { value: number; formatOptions?: Intl.NumberFormatOptions }) => React.ReactNode)-Custom content or render function that replaces the animated display.
classNamestring-CSS classes for styling.

NumberFieldRenderProps

When using render props with className, style, or children, these values are available:

PropTypeDescription
isDisabledbooleanWhether the field is disabled.
isInvalidbooleanWhether the field is currently invalid.
isReadOnlybooleanWhether the field is read-only.
isRequiredbooleanWhether the field is required.
isFocusedbooleanWhether the field is currently focused (DEPRECATED - use isFocusWithin).
isFocusWithinbooleanWhether any child element is focused.
isFocusVisiblebooleanWhether focus is visible (keyboard navigation).
valuenumber | undefinedCurrent value.
minValuenumber | undefinedMinimum allowed value.
maxValuenumber | undefinedMaximum allowed value.
stepnumberStep value for increment/decrement.

On this page