Format numbers in input fields using React

Large numbers are easier to read if they are formatted with thousand separators, 1 000 000 rather than 1000000. When the number is going to be entered into an input field, it’s a good idea to use an input of type number. In this example we build a React component for formatted number input

We’re starting with an input field with type=number. Number input fields provide built-in validation, and arrow controls for incrementing the number. However, they don’t allow us to format the number with thousand delimiters since the input must be numerical.

import React from "react";
import PropTypes from "prop-types";

const NumberField = props => {
  const onChange = event => {
    props.onChange(event.target.value);
  };

  return (
    <div>
      <label htmlFor={props.name}>Income</label>
      <input
        type="number"
        name={props.name}
        value={props.value}
        onChange={onChange}
      />
    </div>
  );
};

NumberField.propTypes = {
  name: PropTypes.string,
  value: PropTypes.string,
  onChange: PropTypes.func
};

export default NumberField;

Conditionally render a text input field

In order to format the number, we’re going to need an input field of type text. We still want the user to enter their number in a number input field, so we decide to display the number input as the user focuses the field, and then switch to displaying the text field as the user clicks outside the field or press the tab key. The text field should be read-only.

<input type="text" name={props.name} value={props.value} readOnly />

To know which input field to display, our component will require local state. We need to turn our functional component into a stateful component. Let’s call our state property isEditing.

class NumberField extends React.Component {
  constructor(props) {
    super(props);
    this.state = { isEditing: false };
  }

  onChange(event) {
    this.props.onChange(event.target.value);
  }

  toggleEditing() {
    this.setState({ isEditing: !this.state.isEditing });
  }

  render() {
    return (
      <div>
        <label htmlFor={this.props.name}>Income</label>
        {this.state.isEditing ? (
          <input
            type="number"
            name={this.props.name}
            value={this.props.value}
            onChange={this.onChange.bind(this)}
            onBlur={this.toggleEditing.bind(this)}
          />
        ) : (
          <input
            type="text"
            name={this.props.name}
            value={this.props.value}
            onFocus={this.toggleEditing.bind(this)}
            readOnly
          />
        )}
      </div>
    );
  }
}

Format number using Intl

We’re now ready to format the content of our text input field. JavaScript’s built in Intl.NumberFormat makes it easy to do language sensitive number formatting. We add a function to our component for formatting the number. Notice the option style: "decimal", also check out style: "currency", if you prefer to display your currency unit with your number.

toCurrency(number) {
  const formatter = new Intl.NumberFormat("sv-SE", {
    style: "decimal",
    currency: "SEK"
  });

  return formatter.format(number);
}

Finished component

Our finished component looks like this.

import React from "react";
import PropTypes from "prop-types";

class NumberField extends React.Component {
  constructor(props) {
    super(props);
    this.state = { isEditing: false };
  }

  onChange(event) {
    this.props.onChange(event.target.value);
  }

  toCurrency(number) {
    const formatter = new Intl.NumberFormat("sv-SE", {
      style: "decimal",
      currency: "SEK"
    });

    return formatter.format(number);
  }

  toggleEditing() {
    this.setState({ isEditing: !this.state.isEditing });
  }

  render() {
    return (
      <div>
        <label htmlFor={this.props.name}>Income</label>
        {this.state.isEditing ? (
          <input
            type="number"
            name={this.props.name}
            value={this.props.value}
            onChange={this.onChange.bind(this)}
            onBlur={this.toggleEditing.bind(this)}
          />
        ) : (
          <input
            type="text"
            name={this.props.name}
            value={this.toCurrency(this.props.value)}
            onFocus={this.toggleEditing.bind(this)}
            readOnly
          />
        )}
      </div>
    );
  }
}

NumberField.propTypes = {
  name: PropTypes.string,
  value: PropTypes.string,
  onChange: PropTypes.func
};

export default NumberField;

In the browser it will appear as if the number is formatted on blur. While we could have used a text input and formatted it on the change event, this example enables us to make use of the number input field’s built-in validation and controls.