Format Phone Number with React

This article addresses the problem of formatting phone number inputs into a given format (555) 123-1234 using React.

You can take a look on this demo.

Firstly, we'll create our component and import it into the App.tsx file.

// App.tsx file
import "./App.css";
import PhoneNumberInput from "./components/PhoneNumberInput";

function App() {
  return <PhoneNumberInput />;
}

export default App;

The PhoneNumberInput component:

// PhoneNumberInput component
import { useState } from "react";
import { enforceFormat, formatToPhone } from "./helpers";

const PhoneNumberInput = () => {
  const [phoneValue, setPhoneValue] = useState<string | undefined>("");
  return (
    <input
      type="text"
      maxLength={16}
      onKeyDown={enforceFormat}
      onChange={(event) => setPhoneValue(formatToPhone(event))}
      value={phoneValue}
    />
  );
};

export default PhoneNumberInput;

For the format of the input, we have 2 handler functions: enforceFormat and formatToPhone

The first function enforceFormat, prevents any non-numeric and non-essential keys from affecting the input, ensuring that the user can only enter valid phone number characters and use necessary editing shortcuts.

  • isNumericInput function checks if the pressed key is a numeric digit, allowing only numbers to be entered.

  • isModifierKey function identifies and permits essential keys such as Shift, Backspace, Tab, Enter, Delete, arrow keys, and common shortcut keys (e.g., Ctrl+A, Ctrl+C).

// helpers.ts file
import { ChangeEvent, KeyboardEvent } from "react";

export const isNumericInput = (event: KeyboardEvent<HTMLInputElement>) => {
  const code = event.code;
  return (
    code === "Digit1" ||
    code === "Digit2" ||
    code === "Digit3" ||
    code === "Digit4" ||
    code === "Digit5" ||
    code === "Digit6" ||
    code === "Digit7" ||
    code === "Digit8" ||
    code === "Digit9" ||
    code === "Digit0"
  );
};

export const isModifierKey = (event: KeyboardEvent<HTMLInputElement>) => {
  const code = event.code;
  return (
    event.shiftKey === true ||
    code === "Backspace" ||
    code === "ShiftRight" ||
    code === "ShiftLeft" ||
    code === "Tab" ||
    code === "Enter" ||
    code === "Delete" || // Allow Shift, Backspace, Tab, Enter, Delete
    code === "ArrowRight" ||
    code === "ArrowLeft" ||
    // Allow Ctrl/Command + A,C,V,X,Z
    ((event.ctrlKey === true || event.metaKey === true) &&
      (code === "KeyA" ||
        code === "KeyC" ||
        code === "KeyV" ||
        code === "KeyX" ||
        code === "KeyZ"))
  );
};

export const enforceFormat = (event: KeyboardEvent<HTMLInputElement>) => {
  if (!isModifierKey(event) && !isNumericInput(event)) {
    event.preventDefault();
  }
};

The formatToPhone function is designed to transform user input into a standardized phone number format.

It begins by stripping all non-numeric characters from the input and limiting the input to the first ten digits, which are sufficient for a standard U.S. phone number.

The function then divides these digits into three parts: areaCode, the middle three digits, and the last four digits. Depending on the number of digits entered, the function assembles and returns a formatted phone number string in the form (555) 123-1234.

// helpers.ts file
export const formatToPhone = (event: ChangeEvent<HTMLInputElement>) => {
  const input = event.target.value.replace(/\D/g, "").substring(0, 10); // First ten digits of input only

  const areaCode = input.substring(0, 3);
  const middle = input.substring(3, 6);
  const last = input.substring(6, 10);

  let phoneNumber = "";

  if (input.length > 6) {
    phoneNumber = `(${areaCode}) ${middle} - ${last}`;
  } else if (input.length > 3) {
    phoneNumber = `(${areaCode}) ${middle}`;
  } else if (input.length > 0) {
    phoneNumber = `(${areaCode}`;
  }

  return phoneNumber;
};

And just like that, you've turned a string of chaotic digits into a well-behaved, properly formatted phone number!