NumberField allows for numerical input.

also known as Number Input, Spin Box

Figma:

Responsive:

Adaptive:

Props

Component props
Name
Type
Default
id
Required
string
-

A unique identifier for the input.

onChange
Required
(arg1: {
  event: React.ChangeEvent<HTMLInputElement>;
  value: number | undefined;
}) => void
-

Callback triggered when the value of the input changes, whether by keyboard entry or the input's arrows.

autoComplete
"on" | "off"
-

Indicate if autocomplete should be available on the input.

dataTestId
string
-

Available for testing purposes, if needed. Consider better queries before using this prop.

disabled
boolean
false

Indicate if the input is disabled.

errorMessage
React.Node
-

For most use cases, pass a string with a helpful error message (be sure to localize!). In certain instances it can be useful to make some text clickable; to support this, you may instead pass a React.Node to wrap text in Link or TapArea.

helperText
string
-

More information for the user about how to complete the form field.

label
string
-

The label for the input. Be sure to localize the text.

labelDisplay
"visible" | "hidden"
"visible"

Whether the label should be visible or not. If hidden, the label is still available for screen reader users, but does not appear visually.

max
number
-

The upper bound of valid input, inclusive.

min
number
-

The lower bound of valid input, inclusive.

mobileEnterKeyHint
"enter" | "done" | "go" | "next" | "previous" | "search" | "send"
-

Mobile only prop. Optionally specify the action label to present for the enter key on virtual keyboards. See the enterKeyHint variant for more info.

name
string
-

A unique name for the input.

onBlur
(arg1: { event: React.FocusEvent<HTMLInputElement>; value: number | undefined }) => void
-

Callback triggered when the user blurs the input.

onFocus
(arg1: {
  event: React.FocusEvent<HTMLInputElement>;
  value: number | undefined;
}) => void
-

Callback triggered when the user focuses the input.

onKeyDown
(arg1: {
  event: React.KeyboardEvent<HTMLInputElement>;
  value: number | undefined;
}) => void
-

Callback triggered when the user presses any key while the input is focused.

placeholder
string
-

Placeholder text shown the the user has not yet input a value.

readOnly
boolean
false

Indicate if the input is readOnly. See the readOnly example for more details.

ref
ReactElement
-

Ref that is forwarded to the underlying input element.

size
"sm" | "md" | "lg"
"md"

Defines the height of NumberField: sm: 32px, md: 40px (default), lg: 48px. See the size variant for more details.

step
number
-

Indicates the amount the value will increase or decrease when using the input's arrows.

value
number | undefined
-

The current value of the input.

Usage guidelines

When to use
  • Any time the user needs to enter mathematically-relevant numeric values, e.g. the quantity of a product or someone’s age.
When not to use
  • When accepting numerical data that is not mathematically relevant or could have semantic leading 0's, e.g. ZIP codes, phone numbers, social security numbers, etc. Use TextField instead. For telephone numbers specifically, be sure to use type="tel" for the best UX. Check out this blog post for tips on when a number input is a good choice.
  • Situations where text needs to be entered. Use TextField or TextArea instead.

Best practices

Do

Use helper text for important information. Helper text helps users understand how to complete the number field or to indicate any needed input.

Don't

Put essential information in the placeholder text, since it disappears when the user types. The placeholder text is not a replacement for the label.

Do

Always ensure the number field has a visible label. The label provides context and supports users when filling in information.

Don't

Remove the label, as this creates accessibility and usability issues.

Do

Only place related fields on the same line.

Don't

Place unrelated number fields on the same line, as this can create comprehension issues.

Do

Provide clear and useful error messages that help the user fix the issue. Error messages should be displayed in a timely manner — typically once the field loses focus or when the form is submitted.

Don't

Display generic error messages, such as "There is an error".

Do

Consider all text fields as required, unless explicitly noted as optional.

Don't

Mark fields as required.

Accessibility

Comprehension

Be sure to provide instructions to help users understand how to complete the form and use individual form controls.

Labels

Ensure the labels are precise and concise. Labels should only describe the number field they are associated with, and they must be visible.

Validation

When providing a validation message, make sure the instructions are clear and help users complete the field. For example, "Value must be greater than 20". In addition, use the helper text to provide instructions to help users understand how to complete the number field or to indicate any needed input, allowed formats, timing limitations, or other pertinent information.
These practices give screen readers and users of assistive technologies more information about the form, helping them to fill it out.

Keyboard navigation

NumberField has conventional keyboard support.

  • Users relying on the keyboard expect to move focus to each NumberField by using the tab key or shift+tab when moving backwards
  • Users can press the up and down arrow keys to adjust the field value
  • Setting disabled will prevent NumberField from receiving keyboard focus or input

Autofocus

NumberField intentionally lacks support for autofocus. Generally speaking,
autofocus interrupts normal page flow for screen readers making it an
anti-pattern for accessibility.

onSubmit

NumberField is commonly used as an input in forms alongside submit buttons.
In these cases, users expect that pressing Enter or Return with the input
focused will submit the form.

Out of the box, NumberField doesn't expose an onSubmit handler or
individual key event handlers due to the complexities of handling these
properly. Instead, developers are encouraged to wrap NumberField
in a <form> and attach an onSubmit handler to that <form>.

Localization

Be sure to localize all text strings. Note that localization can lengthen text by 20 to 30 percent.

Variants

Size

NumberField can have different sizes. The default size is md (40px). The lg size is 48px. For a dense variant, use the sm (32px) variant.

import { useState } from 'react';
import { Box, Flex, NumberField } from 'gestalt';

export default function Example() {
  const [input1text, setInput1Text] = useState(0);
  const [input2text, setInput2Text] = useState(0);
  const [input3text, setInput3Text] = useState(0);

  return (
    <Box padding={8} width="100%">
      <Flex direction="column" gap={6} width="100%">
        <NumberField
          id="size-example-sm"
          label="Number of widgets"
          onChange={({ value }) => setInput1Text(value)}
          placeholder="Please enter the number of widgets"
          size="sm"
          value={input1text}
        />
        <NumberField
          id="size-example-md"
          label="Number of widgets"
          onChange={({ value }) => setInput2Text(value)}
          placeholder="Please enter the number of widgets"
          size="md"
          value={input2text}
        />
        <NumberField
          id="size-example-lg"
          label="Number of widgets"
          onChange={({ value }) => setInput3Text(value)}
          placeholder="Please enter the number of widgets"
          size="lg"
          value={input3text}
        />
      </Flex>
    </Box>
  );
}

State

  1. Enabled
    The enabled state of NumberField that represents it can be interacted with.

  2. Error
    NumberField can display an error message. Simply pass in an errorMessage when there is an error present and NumberField will handle the rest. Don't use errorMessage to provide feedback on character count errors. See the maximum length variant for more details.

  3. Read-only
    Read-only NumberField are used to present information to the user without allowing them to edit the content. Typically they are used to show content or information that the user does not have permission or access to edit.

  4. Disabled
    NumberField cannot be interacted with using the mouse or keyboard. They also do not need to meet contrast requirements, so do not use them to present info to the user (use "readOnly" instead).

Enabled
import { useState } from 'react';
import { Box, Flex, NumberField } from 'gestalt';

export default function Example() {
  const [input1text, setInput1Text] = useState(0);
  const [input2text, setInput2Text] = useState(0);
  const [input3text, setInput3Text] = useState(0);

  return (
    <Box padding={8} width="100%">
      <Flex direction="column" gap={6} width="100%">
        <NumberField
          id="enabled-example-sm"
          label="Number of widgets"
          onChange={({ value }) => setInput1Text(value)}
          placeholder="Please enter the number of widgets"
          size="sm"
          value={input1text}
        />
        <NumberField
          id="enabled-example-md"
          label="Number of widgets"
          onChange={({ value }) => setInput2Text(value)}
          placeholder="Please enter the number of widgets"
          size="md"
          value={input2text}
        />
        <NumberField
          id="enabled-example-lg"
          label="Number of widgets"
          onChange={({ value }) => setInput3Text(value)}
          placeholder="Please enter the number of widgets"
          size="lg"
          value={input3text}
        />
      </Flex>
    </Box>
  );
}

Disabled
import { useState } from 'react';
import { Box, Flex, NumberField } from 'gestalt';

export default function Example() {
  const [input1text, setInput1Text] = useState(0);
  const [input2text, setInput2Text] = useState(0);
  const [input3text, setInput3Text] = useState(0);

  return (
    <Box padding={8} width="100%">
      <Flex direction="column" gap={6} width="100%">
        <NumberField
          disabled
          id="disabled-example-sm"
          label="Number of widgets"
          onChange={({ value }) => setInput1Text(value)}
          placeholder="Please enter the number of widgets"
          size="sm"
          value={input1text}
        />
        <NumberField
          disabled
          id="disabled-example-md"
          label="Number of widgets"
          onChange={({ value }) => setInput2Text(value)}
          placeholder="Please enter the number of widgets"
          size="md"
          value={input2text}
        />
        <NumberField
          disabled
          id="disabled-example-lg"
          label="Number of widgets"
          onChange={({ value }) => setInput3Text(value)}
          placeholder="Please enter the number of widgets"
          size="lg"
          value={input3text}
        />
      </Flex>
    </Box>
  );
}

Error
import { useState } from 'react';
import { Box, Flex, NumberField } from 'gestalt';

export default function Example() {
  const [input1text, setInput1Text] = useState(undefined);
  const [input2text, setInput2Text] = useState(undefined);
  const [input3text, setInput3Text] = useState(undefined);

  return (
    <Box padding={8} width="100%">
      <Flex direction="column" gap={6} width="100%">
        <NumberField
          errorMessage={
            input1text === null || input1text === undefined
              ? 'You must enter a number'
              : null
          }
          id="error-example-sm"
          label="Number of widgets"
          onChange={({ value }) => setInput1Text(value)}
          placeholder="Please enter the number of widgets"
          size="sm"
          value={input1text}
        />
        <NumberField
          errorMessage={
            input2text === null || input2text === undefined
              ? 'You must enter a number'
              : null
          }
          id="error-example-md"
          label="Number of widgets"
          onChange={({ value }) => setInput2Text(value)}
          placeholder="Please enter the number of widgets"
          size="md"
          value={input2text}
        />
        <NumberField
          errorMessage={
            input3text === null || input3text === undefined
              ? 'You must enter a number'
              : null
          }
          id="error-example-lg"
          label="Number of widgets"
          onChange={({ value }) => setInput3Text(value)}
          placeholder="Please enter the number of widgets"
          size="lg"
          value={input3text}
        />
      </Flex>
    </Box>
  );
}

Read-only
import { useState } from 'react';
import { Box, Flex, NumberField } from 'gestalt';

export default function Example() {
  const [input1text, setInput1Text] = useState(0);
  const [input2text, setInput2Text] = useState(0);
  const [input3text, setInput3Text] = useState(0);

  return (
    <Box padding={8} width="100%">
      <Flex direction="column" gap={6} width="100%">
        <NumberField
          id="readOnly-example-sm"
          label="Number of widgets"
          onChange={({ value }) => setInput1Text(value)}
          placeholder="Please enter the number of widgets"
          readOnly
          size="sm"
          value={input1text}
        />
        <NumberField
          id="readOnly-example-md"
          label="Number of widgets"
          onChange={({ value }) => setInput2Text(value)}
          placeholder="Please enter the number of widgets"
          readOnly
          size="md"
          value={input2text}
        />
        <NumberField
          id="readOnly-example-lg"
          label="Number of widgets"
          onChange={({ value }) => setInput3Text(value)}
          placeholder="Please enter the number of widgets"
          readOnly
          size="lg"
          value={input3text}
        />
      </Flex>
    </Box>
  );
}

Label

'label' is an optional prop; however, NumberField should always be properly labelled. Learn about accessibility best practices regarding labels.

Check TextField's Label variant section for more examples and guidance.

Helper text

Whenever you want to provide more information about a form field, you should use helperText.

import { useState } from 'react';
import { Box, Flex, NumberField } from 'gestalt';

export default function Example() {
  const [currentValue, setCurrentValue] = useState();

  return (
    <Flex
      alignItems="center"
      height="100%"
      justifyContent="center"
      width="100%"
    >
      <Box width={400}>
        <NumberField
          helperText="Round up to the nearest whole number"
          id="variant-helperText"
          label="Average value"
          onChange={({ value }) => {
            setCurrentValue(value);
          }}
          value={currentValue}
        />
      </Box>
    </Flex>
  );
}

Min/max/step

NumberField provides additional props specific to numerical input.

min and max can be used to define the acceptable bounds of the input (see the ref example for more about using these for validation).

step determines the amount incremented or decremented when using the input's arrow buttons. Set this as a float to allow decimal input.

import { useState } from 'react';
import { Flex, NumberField } from 'gestalt';

export default function Example() {
  const [value1, setValue1] = useState();
  const [value2, setValue2] = useState();

  return (
    <Flex
      alignItems="center"
      direction="column"
      gap={4}
      height="100%"
      justifyContent="center"
      width="100%"
    >
      <Flex.Item minWidth="80%">
        <NumberField
          helperText="Use the arrow buttons to increase/decrease the input value"
          id="minMaxStepExampleNumberField1"
          label="Stepping in intervals of 5"
          max={25}
          min={5}
          onChange={({ value }) => {
            setValue1(value);
          }}
          step={5}
          value={value1}
        />
      </Flex.Item>

      <Flex.Item minWidth="80%">
        <NumberField
          helperText="Use the arrow buttons to increase/decrease the input value"
          id="minMaxStepExampleNumberField2"
          label="Stepping in intervals of 0.1"
          max={2}
          min={-2}
          onChange={({ value }) => {
            setValue2(value);
          }}
          step={0.1}
          value={value2}
        />
      </Flex.Item>
    </Flex>
  );
}

EnterKeyHint

The mobileEnterKeyHint prop presents to the user a more accurate action label for the enter key on virtual keyboards. These are the values for each use case:

  • "enter": inserting a new line
  • "done": there is nothing more to input and the input editor will be closed
  • "go": taking the user to the target of the text they typed
  • "next": taking the user to the next field that will accept text
  • "previous": taking the user to the previous field that will accept text
  • "search": taking the user to the results of searching for the text they have typed
  • "send": submitting the input, such as an email or chat
import { useState } from 'react';
import { Box, Flex, NumberField } from 'gestalt';

export default function Example() {
  const [currentValue, setCurrentValue] = useState();

  return (
    <Flex
      alignItems="center"
      height="100%"
      justifyContent="center"
      width="100%"
    >
      <Box width={300}>
        <NumberField
          id="enterKeyHint"
          label="Age"
          mobileEnterKeyHint="next"
          onChange={({ value }) => {
            setCurrentValue(value);
          }}
          value={currentValue}
        />
      </Box>
    </Flex>
  );
}

Refs

Set a ref on NumberField to use the Constraint Validation API or to anchor a Popover-based element.

Note that while the arrow buttons will not exceed the min/max (if set), the user is free to enter any number using the keyboard. Validation should be performed explicitly using the Constraint Validation API to ensure the value is within the specified range.

import { useEffect, useRef, useState } from 'react';
import { Box, Flex, NumberField } from 'gestalt';

export default function Example() {
  const [currentValue, setCurrentValue] = useState();
  const [errorMessage, setErrorMessage] = useState(undefined);
  const ref = useRef(null);

  useEffect(() => {
    if (ref.current && ref.current.checkValidity() === false) {
      setErrorMessage("That episode doesn't exist (yet)!");
    } else {
      setErrorMessage(undefined);
    }
  }, [currentValue]);

  return (
    <Flex
      alignItems="center"
      height="100%"
      justifyContent="center"
      width="100%"
    >
      <Box width={400}>
        <NumberField
          ref={ref}
          errorMessage={errorMessage}
          id="refExampleNumberField"
          label="Enter a Star Wars episode number"
          max={9}
          min={1}
          onChange={({ value }) => {
            setCurrentValue(value);
          }}
          placeholder="Enter a number from 1–9"
          step={2}
          value={currentValue}
        />
      </Box>
    </Flex>
  );
}

Component quality checklist

Component quality checklist
Quality item
Status
Status description
Figma Library
Ready
Component is available in Figma for web and mobile web.
Responsive Web
Ready
Component responds to changing viewport sizes in web and mobile web.

TextField
When accepting numerical data that is not mathematically relevant or could have semantic leading 0's, e.g. ZIP codes, phone numbers, social security numbers, etc. Use TextField instead. For telephone numbers specifically, be sure to use type="tel" for the best UX. Check out this blog post for tips on when a number input is a good choice.