🎁 The Js-Craft Guide to React is now available with a 30% off discount!

Making a React pincode field component

Recently I needed to add something similar to a pincode in a React application. Something like when you enter your pin at the ATM, only that this one could accept also letters, not just numbers.

And of course, I ended up reinventing the wheel, instead of just using a built-in component 🤦‍♂️.

So, in today's article, I will share my approach.

Defining the React pincode component

The requirements:

  • we can pass it a property to set the characters length of the pin
  • it will receive a callback function that will be called when the pin was fully typed
  • if we don't start on the first digit, we are forced to the first one
  • it will auto-focus the next digit after a digit was typed

The end result will work as in this video:

React components Structure

We will have two React components:

1. the actual <Pincode> component that will receive the callback function for when the pin was fully typed and the total length of the pin:

const PinCode = ({ length, onPinEntered }) => { }

const App = ()=> {
  return (
    <div className="App">
      <PinCode 
            length={5} 
            onPinEntered={(pin) => alert("Pin : " + pin)} />
    </div>
  );
}

2. the <Digit> component will also have a callback function for when a digit is typed, a ref object, and a num index value that we will use to move and focus the next digit:

const Digit = forwardRef(({ num, onDigitAdded }, ref) => {})

Building the React pincode component

The first step will be just to render the digits inputs in a loop, based on the wanted length of the final pin:

const PinCode = ({ length, onPinEntered }) => {
  let [pin, setPin] = useState("");
  const onDigitAdded = (d) => {
    setPin(pin + d.value);
    focusNextDigit(d);
  };

  return (
    <div onClick={onClickHandler}>
      {[...Array(length).keys()].map((i) => (
        <Digit
          key={i}
          num={i}
          onDigitAdded={onDigitAdded}
        />
      ))}
    </div>
  );
};

At this point the component will look as follows:
React pincode filed unstyled

Using the onDigitAdded() function we add digits to the state variable pin.

When the pin var has the same size as the lenght property of the PinCode component then it means we have fully typed the pin and we will pass it to the callback function.

const PinCode = ({ length, onPinEntered }) => {
  // ...
  useEffect(() => {
    if (pin.length === length) {
      onPinEntered(pin);
    }
  }, [pin, onPinEntered, length]);
}

The overall structure of the Digit component is pretty simple:

const Digit = forwardRef(({ num, onDigitAdded }) => {
  const keyUpHandler = (e) => onDigitAdded(e.target);
  return (
    <input
      onKeyUp={keyUpHandler}
      type="password"
      maxLength="1"
      data-index={num}
    />
  );
});

Working with multiple React refs to manage the focus of the PinCode Component

You may wonder why the Digit component is wrapped in the forwardRef() hook!?

Well, initially my first attempt was to use the querySelectorAll() function to manage the focus of the multiple-digit inputs.

However, realized that it's not a good idea to use querySelectorAll() in React as it will mess up the Virtual DOM.

Therefore, the next try was to add refs to each Digit component:

const PinCode = ({ length, onPinEntered }) => {
  const digits = useRef([]);
  // ...
  return (
    <div onClick={onClickHandler}>
      {[...Array(length).keys()].map((i) => (
        <Digit
          ref={(d) => digits.current.push(d)}
    // ...
  );

And now we can implement the focusNextDigit() function in the PinCode component:

const focusNextDigit = (d) => {
    const index = parseInt(d.dataset.index);
    const nextDigit = digits.current[index + 1];
    nextDigit?.focus();
}; 

And also, if the user does not start on the first digit, we will auto-move it there:

const onClickHandler = () => {
    if (pin !== "") {
      setPin("");
      digits.current.forEach((d) => {
        if (d) d.value = "";
      });
    }
    digits.current[0].focus();
};

Wrote more here about how to use an array of components with the useRef() hook.

At this point, all the functionality should work as expected.

Our example can be improved by adding support for deleting characters or for pasting codes from the clipboard. In order to be able to paste codes in this component you can use this post as a starting point, and I've also written an intro on how to add copy-paste support for React components.

Styling the PinCode React Component

The final step will be to add some CSS. Fortunately, this step was pretty simple:

body {
  font-family: sans-serif;
  text-align: center;
  background-color: orangered;
}

input {
  width: 50px;
  height: 50px;
  margin: 0.5rem;
  padding: 0.5rem;
  border: 2px solid white;
  text-align: center;
  font-size: 5rem;
  color: white;
  background-color: orangered;
}

After this, we will the final output:
React pincode field component

Full code and live example

You can checkout on my Github the full code and see the live example here.

10 Javascript AI projects with Langchain & React

Not sure what to build? Dive in with these Javascript AI projects! Learn how to build LLM powered apps using Langchain.js and React! Connect your apps to LLMs such as OpenAI, create agents, use vector databases, or setup AI context.

10 Javascript AI projects with Langchain & React

Not sure what to build? Dive in with these Javascript AI projects! Learn how to build LLM powered apps using Langchain.js and React! Connect your apps to LLMs such as OpenAI, create agents, use vector databases, or setup AI context.


Leave a Reply

Your email address will not be published. Required fields are marked *

📘 The Guide to React Home Screencasts Best of Newsletter Search X

📘 - 10 Javascript AI projects with React and LangChain

Hi friend! Before you go, just wanted to let you know about the 10 Javascript AI projects ideas with React and LangChain FREE ebook.

One of the best ways to learn is by doing the work. Choose from these 10 project ideas and start working on topics such as:

  • How does LangChain work
  • Connecting to OpenAI LLM
  • Create AI Agents
  • Simple and Sequential Chains
  • Adding Memory (Chat Context)
  • Prompt Templates
  • Using OutputParsers and Tools for Agents
  • Work with Documents and more...

Keep building and see the new capabilities LLM models can add to your React app! Get all projects as an ebook right to your inbox!

X