Create a passcode component from scratch in React
Tags: react, reactnative, javascript, opensource
Published: 2023-07-16 13:46:45

## Introduction
Have you ever wondered how the one-time password screens on an app or webpage work? These are the screens that have multiple input elements that accept only one character. All these elements behave as if they are one element and have a behavior similar to that of the normal input element. We can call this a passcode component because a one-time password is nothing but a passcode.

In this blog post, we are going to talk about how to create this passcode component. We are going to understand how this component works and the various use cases involved with it. Later in the post, we start with the implementation of the component. 

So without further ado, let us get started.

## What is a Passcode component?
A Passcode component is a group of input boxes. Each character in this component has its own input element and collectively we call this the passcode component. A passcode component looks like the below:


![live-demo-example](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oijs2ii289pm3g1xk36b.gif)



It is used by most of the mobile and web applications these days. It is a common UI practice that is being followed during any validation flow. But it is trickier to implement since it's not a simple input element but is a group of input elements. There are multiple use case scenarios that need to be managed like handling key events, pasting experience, making sure the focus is changing as expected, etc.

Now let us start with understanding the use cases involved in the passcode component. So without further ado let us get started.

## Cases involved in a Passcode component
Following are the basic scenarios that we are going to cover while building the Passcode component:
* All the input boxes should accept only one character
* Once a character is pressed the focus should shift to the next input element
* While backspacing the focus should shift from right to left input element

We are also going to cover one advanced scenario: Pasting experience. It involves the following sub-cases:
* Check for user permissions
* Focus should go to the last input element when the pasted value length is equal to the number of input elements.
* The focus should shift to the input element that contains the last character of the pasted value when pasted value's length is less than the total number of elements.
* The focus should still be on the last input element when the pasted value length is greater than the total number of input elements
* If a user tries to paste the value from an input element that is in between the start and end then, the pasted value is partially filled till the end of the input elements.

## Setting up the project
* We will be using the [create-react-app's typescript template](https://create-react-app.dev/docs/getting-started/#creating-a-typescript-app) to initialize our project. Run the below command to create a project. Make sure to specify the name of the project in the placeholder: <name-of-the-project>

    ```
    npx create-react-app <name-of-the-project> --template typescript
    ```

* Once the project is initialized, we add a couple of folders and files in it.

    ```
    ├── src
    │   ├── App.css
    │   ├── App.test.tsx
    │   ├── App.tsx
    │   ├── components
    │   │   └── Passcode.tsx
    │   ├── index.css
    │   ├── index.tsx
    │   ├── logo.svg
    │   ├── react-app-env.d.ts
    │   ├── reportWebVitals.ts
    │   ├── setupTests.ts
    │   └── utils
    │       └── index.ts
    ```

* We create 2 new folders in it namely: `components` `utils`. We also create the files mentioned in all these folders. We will later take a look at what each of these files means.

* Once these folders are created make sure to start your project by running the following command:
    ```
    yarn start
    ```

## Building the Passcode component

To build a passcode component first we need to understand how it works. Refer to the below diagram to understand it's working.


![arch-diagram](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l87v9cw5io97vs55snzf.gif)


Consider the above diagram. Each box shown above references each input element in the passcode component. If you enter a value inside the first input then two operations happen:
* The value state variable is updated and,
* The focused state gets updated.

We make use of various event handlers such as `onChange`, `onKeyDown`, `onKeyUp`, etc. to manage the focus between multiple input elements and the change in the value state variable. We will take a closer look at how these event handlers are orchestrated in the next section.

At this point, both the focused index and the value props are updated. This triggers a re-render and thus renders the passcode component with the next input element to be focused.

So this is how our component is going to work in laymen's terms.

Now that we have covered the basics, we can move on to addressing each use case mentioned above

## Use case implementation
We will start by creating the scaffoldings for our project. Follow along for the same:

* First, we will create a passcode component. This component acts as a container component. It will hold the data and it will render each input component.
    ```tsx
    const Passcode = () => {
    const [arrayValue, setArrayValue] = useState<(string | number)[]>(['', '', '','']);
    const [currentFocusedIndex, setCurrentFocusedIndex] = useState(0);

    return (
    <>
        <div> currentFocusedIndex: {currentFocusedIndex}</div>

        {arrayValue.map((value: string | number, index: number)=> (
        <input
            key={`index-${index}`}
            type="text"
            value={String(value)}
        />
        ))}
        </>
    );
    }
    ```
* The passcode component created has two state variables: `arrayValue` and `currentFocusedIndex`. `arrayValue` contains the actual value of the passcode component. If any of the underlying input elements change their value then this state variable is also updated. We also have `currentFocusedIndex` that contains the current index of the input element that needs to be focused. This gets updated whenever the user clicks on the input element or types in the input element.

### Case 1: All the input boxes should accept only one character
Now let us start with our first use case. To implement this case we just need to add `maxLength` its value being set to `1`. This allows us to accept only one character.

```tsx
const Passcode = () => {
    const [arrayValue, setArrayValue] = useState<(string | number)[]>(['', '', '','']);
    const [currentFocusedIndex, setCurrentFocusedIndex] = useState(0);

    return (
    <>
        <div> currentFocusedIndex: {currentFocusedIndex}</div>

        {arrayValue.map((value: string | number, index: number)=> (
        <input
            key={`index-${index}`}
            maxLength={1}
            type="text"
            value={String(value)}
        />
        ))}
        </>
    );
    }
```

This is an optional step, but if you want to control what type of keyboard to be displayed when the users fill this component and only want to allow numerals to be entered then simply add the `inputMode` attribute and set it to numeric. This will make sure that a numeric virtual keyboard is displayed when the user types via mobile screen:

```tsx
    <input  
        key={`index-${index}`}
        inputMode="numeric"
        pattern="\d{1}"
        maxLength={1}
        type="text"
        value={String(value)}
      />

```

### Case 2: Once a character is typed the focus should shift to the next input box
By far this is going to be the most crucial use case to solve because this use case defines the basic interaction of this component. To solve it we need to consider a couple of things:
* To control the focus of each input element we need to have an actual control of the input element. To achieve this, we need to pass the ref attribute to each input element.
* We are going to segregate the task of focus and updating arrayValue the attribute in different event handlers:
    - `onChange` - will handle the update part of arrayValue state variable
    - `onKeyup` - will be responsible to update the focused index state variable.
    - `onKeyDown` - will be responsible to prevent typing any other keys except for the numerics.
    - `onFocus` - will make sure that we update the current element's focus when clicked.

Let us start by adding `ref` attribute to each input element. To do this, we should create an array as a reference. This array will store the reference of each input element.

* To do this we declare an array as a reference value as follows:

    ```tsx
    const inputRefs = useRef<Array<HTMLInputElement> | []>([]);
    ```
* Next, we make sure that we add a reference of each element into the array by doing the following:

    ```tsx
        <input
            key={`index-${index}`}
            ref={(el) => el && (inputRefs.current[index] = el)} // here
            inputMode="numeric"
            maxLength={1}
            type="text"
            value={String(value)}
        />
    ```

We use the `ref` callback mechanism to get the current element. You can read more about `ref` callback function here. Next, we assign this current element at the ith index of `inputRefs` array.

In this way, we store the reference of each input element in an array of `ref`. Now let us take a closer look at how we are going to use these references.

A passcode component only accepts a single numeric character in each of its input elements. We just need to restrict our passcode component so as to not accept any non-numeric values. In some cases, it can also accept alphanumeric code value but that's currently out of the scope of this blogpost. On the overall component, we are trying to restrict users by only typing numeric values in each input element. What we can do is simply prevent the default behavior of the key event on specific keystrokes. Here is what we will do:

```tsx
const onKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
  const keyCode = e.key;
        if (!(keyCode >= 0 && keyCode <= 9)) {
          e.preventDefault();
        }
      };
```

The above code ensures that the default behavior, which is preventing keystrokes for all non-numeric values, is implemented. We determine whether the key pressed is numeric or not using `e.key`. The Keyboard events `key` attribute returns the value of the key pressed by the user. Since we want to allow this behavior, we do not execute `e.preventDefault()` for non-numeric values. Therefore, this method helps us restrict the typing to only numeric values.

Next, we understand how to update the state variable `arrayValue`. We update it when `onChange` the event occurs. Here is how you can update this state:

```tsx
const onChange = (e: BaseSyntheticEvent, index: number) => {
    setArrayValue((preValue: (string | number)[]) => {
      const newArray = [...preValue];

      if (parseInt(e.target.value)) {
        newArray[index] = parseInt(e.target.value);
      } else {
        newArray[index] = e.target.value;
      }

      return newArray;
    });
  };
```

Here we make sure that the value provided by the `onChange` event is numeric or not. If yes, we set it in the clone variable `newArray`.
Next, we have a look at the `onKeyUp` event handler. In this event handler, we update the state variable `currentFocusedIndex`. Below is the code that helps you update this state variable:

```tsx
const onKeyUp = (e: KeyboardEvent<HTMLInputElement>, index: number) => {
    if (parseInt(e.key) && index < arrayValue.length - 1) {
      setCurrentFocusedIndex(index + 1);
      if (inputRefs && inputRefs.current && index === currentFocusedIndex) {
        inputRefs.current[index + 1].focus();
      }
    }
  };
```

Here the first update that we do is to set the `currentFocusedIndex` value to `index+1`. Next, we set the next input element into the focused state with the help of the `focus` function. In this way, the next element is focused as well as the code is now ready to handle the same set of events again for the next input element.

Finally, we have the `onFocus` event handler. The purpose of this handler is to update the `currentFocusedIndex` index of the input element that is being manually focused by a click event.

```tsx
const onFocus = (e: BaseSyntheticEvent, index: number) => {
        setCurrentFocusedIndex(index);
      };
```

We stitch all these handlers together and this is how our passcode component will look like:

```tsx
const Passcode = () => {
  const [arrayValue, setArrayValue] = useState<(string | number)[]>([
    "",
    "",
    "",
    ""
  ]);
  const [currentFocusedIndex, setCurrentFocusedIndex] = useState(0);
  const inputRefs = useRef<Array<HTMLInputElement> | []>([]);

  const onKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
  const keyCode = e.key;
        if (!(keyCode >= 0 && keyCode <= 9)) {
          e.preventDefault();
        }
      };

  const onChange = (e: BaseSyntheticEvent, index: number) => {
    setArrayValue((preValue: (string | number)[]) => {
      const newArray = [...preValue];

      if (parseInt(e.target.value)) {
        newArray[index] = parseInt(e.target.value);
      } else {
        newArray[index] = e.target.value;
      }

      return newArray;
    });
  };

  const onKeyUp = (e: KeyboardEvent<HTMLInputElement>, index: number) => {
    if (parseInt(e.key) && index <= arrayValue.length - 2) {
      setCurrentFocusedIndex(index + 1);
      if (inputRefs && inputRefs.current && index === currentFocusedIndex) {
        inputRefs.current[index + 1].focus();
      }
    }
  };

  const onFocus = (e: BaseSyntheticEvent, index: number) => {
    setCurrentFocusedIndex(index);
    e.target.focus();
  };
  return (
    <>
      <div> currentFocusedIndex: {currentFocusedIndex}</div>
      <div>{arrayValue}</div>
      {arrayValue.map((value: string | number, index: number) => (
        <input
          key={`index-${index}`}
          ref={(el) => el && (inputRefs.current[index] = el)}
          inputMode="numeric"
          pattern="\d{1}"
          maxLength={1}
          type="text"
          value={String(value)}
          onChange={(e) => onChange(e, index)}
          onKeyUp={(e) => onKeyUp(e, index)}
          onKeyDown={(e) => onKeyDown(e)}
          onFocus={(e) => onFocus(e, index)}
        />
      ))}
    </>
  );
};
```

### Case 3: While backspacing the focus should shift from right to left

This is a pretty common use case where the user wants to clear each value that is entered with the press of the `backspace` key. To achieve this we need to make sure that we update the `currentFocusedIndex` in `onKeyUp` event. We also need to allow the pressing of the `Backspace` key, to do that we add a condition inside the `onKeyDown` event.

Update the `onKeyUp` and `onKeyDown` event handler as follows:

```tsx
const onKeyUp = (e: KeyboardEvent<HTMLInputElement>) => {
        if (e.key === "Backspace") {
          if (index === 0) {
            setCurrentFocusedIndex(0);
          } else {
            setCurrentFocusedIndex(index - 1);
            if (
              inputRefs &&
              inputRefs.current &&
              index === currentForcusedIndex
            ) {
              inputRefs.current[index - 1].focus();
            }
          }
        } else {
          if (
            (parseInt(e.key)) &&
            index <= array.length - 2
          ) {
            setCurrentFocusedIndex(index + 1);
            if (
              inputRefs &&
              inputRefs.current &&
              index === currentForcusedIndex
            ) {
              inputRefs.current[index + 1].focus();
            }
          }
        }
      };

const onKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
    const keyCode = e.key;
    if (
      !(keyCode >= 0 && keyCode <= 9) &&
      keyCode !== "Backspace" 
    ) {
      e.preventDefault();
    }
  };
```

Here we added a block that handles the `currentFocusedIndex` and the focus of the input element when the Backspace key is pressed. First, if the current index is zero we also update the `currentFocusedIndex` to it else we set it to `index-1` i.e. the focus is set to the previous element. We also make sure that we focus on the previous element by calling the focus function of the previous element. Inside the `onKeyDown` event handler, we added a condition to not execute `preventDefault` the function when `keyCode` is `Backspace`. This makes sure that the backspace key can be pressed and performs its default behavior.

### Case 4: Pasting experience

This use case has multiple sub-use cases.

    a. Check for user permissions

    b. Focus should go to the last input box when the pasted value length is equal to the number of input boxes

    c. The focus should shift to the input box that contains the last character of the pasted value when pasted value's length is less than the total number of input elements

    d. The focus should be on the last input box when the pasted value length is greater than the total number of boxes

    e. If a user tries to paste the value from an input box that is in between the start and end then, the pasted value is partially filled till the end of the input boxes


![pasting-exp-cases](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0xk42tryhp3wr62vjewj.png)


Now let us get started with the implementation of the above sub-use cases. The `Pasting Experiences` scenario is carried out entirely inside the useEffect hook. We add an event listener to the `paste` event.

Here is what our code will look like:

```tsx
useEffect(() => {
 document.addEventListener("paste", async () => {
  // Handle all sub-usecases here
 });
}, [])
```

To allow the pasting of values we need to allow the pressing of `CRTL + V` key combinations so that users can paste the value via the keyboard. We need to add this condition inside `onKeyDown` event handler to allow this key combination:

```tsx
const onKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
    const keyCode = parseInt(e.key);
    if (
      !(keyCode >= 0 && keyCode <= 9) &&
      e.key !== "Backspace" &&
      !(e.metaKey && e.key === "v")
    ) {
      e.preventDefault();
    }
  };
```

### Case a: Check user permission
To check for permission of pasting we make use of the Navigator interface's permission read-only property. You can read more about this property [here](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/permissions). We make use of the query function to get the current permission. In our scenario, we are required to get the `clipboard-read` permission. We use the below code to do the same:

```tsx
useEffect(() => {
    document.addEventListener("paste", async () => {
      // Handle all sub-usecases here

      const pastePermission = await navigator.permissions.query({
        name: "clipboard-read" as PermissionName
      });

      if (pastePermission.state === "denied") {
        throw new Error("Not allowed to read clipboard");
      }
    });
  }, []);
```

With this code, we make sure that we ask for the user's permission before pasting the data. The permission dialog box will look like the below:


![permission-dialog-box](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/yx4z984em5jyw894216b.png)


Next, we manage the focus of the passcode component while pasting the numeric value. For cases: b, c, d, and e we are going to manage them in one go since all of them are related to focus management. Before we start with pasting experience and focus management we should make sure that all the values from the clipboard are numbers. But before that, we should read the content from the clipboard:

```tsx
const clipboardContent = await navigator.clipboard.readText();
```

Next, we convert all the content from the clipboard to a number:

```tsx
try {
        let newArray: Array<number | string> = clipboardContent.split("");
        newArray = newArray.map((num) => Number(num));
      } catch (err) {
        console.error(err);
      }
```

To create a pasting experience, we update `arrayValue` the contents of the clipboard. We update the `arrayValue` but based on the `currentFocusedIndex`:
* If `currentFocusedIndex > 0` i.e. if pasting gets started from any input element except the first and the last input element, then we need to fill the arrayValue from the focused input element to the last input element.
    - We first calculate the number of places/input elements that are available from the currently focused index to the last input element. We call this variable as remainingPlaces.
    - Now we know that we need to fill these many places of the arrayValue, we slice the pasting array from 0 to the remaining places.
    - Now we create the new array which is a merge of: arrayValue sliced from 0 to currentFocusedIndex and then the partially filled array i.e. the sliced-pasted array.
    - The code for this looks like below:
    ```tsx
    const lastIndex = arrayValue.length - 1;
        if (currentFocusedIndex > 0) {
          const remainingPlaces = lastIndex - currentFocusedIndex;
          const partialArray = newArray.slice(0, remainingPlaces + 1);
          setArrayValue([
            ...arrayValue.slice(0, currentFocusedIndex),
            ...partialArray
          ]);
        }
    ```
    - If `currentFocusedIndex = 0`, then we do set the arrayValue array like below:
    ```tsx
    setArrayValue([
            ...newArray,
            ...arrayValue.slice(newArray.length - 1, lastIndex)
          ]);
    ```

Once we update the `arrayValue` field, now it is time that we update the `currentFocusedIndex`. We update the `currentFocusedIndex` and the input element based on the following condition:

* If the pasting array's length is less than the length of the arrayValue and the current focused index is the first input element then we update the focus of that input element which contains the last element of the pasting array.
* Else we update the focus of the last input element:

```tsx
if (newArray.length < arrayValue.length && currentFocusedIndex === 0) {
          setCurrentFocusedIndex(newArray.length - 1);
          inputRefs.current[newArray.length - 1].focus();
        } else {
          setCurrentFocusedIndex(arrayValue.length - 1);
          inputRefs.current[arrayValue.length - 1].focus();
        }
```

We stitch all these changes inside the useEffect hook like below:
```tsx
useEffect(() => {
    document.addEventListener("paste", async () => {
      // Handle all sub-usecases here

      const pastePermission = await navigator.permissions.query({
        name: "clipboard-read" as PermissionName
      });

      if (pastePermission.state === "denied") {
        throw new Error("Not allowed to read clipboard");
      }

      const clipboardContent = await navigator.clipboard.readText();
      try {
        let newArray: Array<number | string> = clipboardContent.split("");
        newArray = newArray.map((num) => Number(num));

        const lastIndex = arrayValue.length - 1;
        if (currentFocusedIndex > 0) {
          const remainingPlaces = lastIndex - currentFocusedIndex;
          const partialArray = newArray.slice(0, remainingPlaces + 1);
          setArrayValue([
            ...arrayValue.slice(0, currentFocusedIndex),
            ...partialArray
          ]);
        } else {
          setArrayValue([
            ...newArray,
            ...arrayValue.slice(newArray.length - 1, lastIndex)
          ]);
        }

        if (newArray.length < arrayValue.length && currentFocusedIndex === 0) {
          setCurrentFocusedIndex(newArray.length - 1);
          inputRefs.current[newArray.length - 1].focus();
        } else {
          setCurrentFocusedIndex(arrayValue.length - 1);
          inputRefs.current[arrayValue.length - 1].focus();
        }
      } catch (err) {
        console.error(err);
      }
    });

    return () => {
      document.removeEventListener("paste", () =>
        console.log("Removed paste listner")
      );
    };
  }, [arrayValue, currentFocusedIndex]);
```

We have completed the implementation of our passcode component. Here is what the passcode component will look like:
```tsx
const Passcode = () => {
  const [arrayValue, setArrayValue] = useState<(string | number)[]>([
    "",
    "",
    "",
    ""
  ]);
  const [currentFocusedIndex, setCurrentFocusedIndex] = useState(0);
  const inputRefs = useRef<Array<HTMLInputElement> | []>([]);

  const onKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
    const keyCode = parseInt(e.key);
    if (
      !(keyCode >= 0 && keyCode <= 9) &&
      e.key !== "Backspace" &&
      !(e.metaKey && e.key === "v")
    ) {
      e.preventDefault();
    }
  };

  const onChange = (e: BaseSyntheticEvent, index: number) => {
    setArrayValue((preValue: (string | number)[]) => {
      const newArray = [...preValue];

      if (parseInt(e.target.value)) {
        newArray[index] = parseInt(e.target.value);
      } else {
        newArray[index] = e.target.value;
      }

      return newArray;
    });
  };

  const onKeyUp = (e: KeyboardEvent<HTMLInputElement>, index: number) => {
    if (e.key === "Backspace") {
      if (index === 0) {
        setCurrentFocusedIndex(0);
      } else {
        setCurrentFocusedIndex(index - 1);
        if (inputRefs && inputRefs.current && index === currentFocusedIndex) {
          inputRefs.current[index - 1].focus();
        }
      }
    } else {
      if (parseInt(e.key) && index < arrayValue.length - 1) {
        setCurrentFocusedIndex(index + 1);
        if (inputRefs && inputRefs.current && index === currentFocusedIndex) {
          inputRefs.current[index + 1].focus();
        }
      }
    }
  };

  const onFocus = (e: BaseSyntheticEvent, index: number) => {
    setCurrentFocusedIndex(index);
    // e.target.focus();
  };

  useEffect(() => {
    document.addEventListener("paste", async () => {
      // Handle all sub-usecases here

      const pastePermission = await navigator.permissions.query({
        name: "clipboard-read" as PermissionName
      });

      if (pastePermission.state === "denied") {
        throw new Error("Not allowed to read clipboard");
      }

      const clipboardContent = await navigator.clipboard.readText();
      try {
        let newArray: Array<number | string> = clipboardContent.split("");
        newArray = newArray.map((num) => Number(num));

        const lastIndex = arrayValue.length - 1;
        if (currentFocusedIndex > 0) {
          const remainingPlaces = lastIndex - currentFocusedIndex;
          const partialArray = newArray.slice(0, remainingPlaces + 1);
          setArrayValue([
            ...arrayValue.slice(0, currentFocusedIndex),
            ...partialArray
          ]);
        } else {
          setArrayValue([
            ...newArray,
            ...arrayValue.slice(newArray.length - 1, lastIndex)
          ]);
        }

        if (newArray.length < arrayValue.length && currentFocusedIndex === 0) {
          setCurrentFocusedIndex(newArray.length - 1);
          inputRefs.current[newArray.length - 1].focus();
        } else {
          setCurrentFocusedIndex(arrayValue.length - 1);
          inputRefs.current[arrayValue.length - 1].focus();
        }
      } catch (err) {
        console.error(err);
      }
    });

    return () => {
      document.removeEventListener("paste", () =>
        console.log("Removed paste listner")
      );
    };
  }, [arrayValue, currentFocusedIndex]);

  return (
    <>
      <div> currentFocusedIndex: {currentFocusedIndex}</div>
      <div>{arrayValue}</div>
      {arrayValue.map((value: string | number, index: number) => (
        <input
          key={`index-${index}`}
          ref={(el) => el && (inputRefs.current[index] = el)}
          inputMode="numeric"
          maxLength={1}
          type="text"
          value={String(value)}
          onChange={(e) => onChange(e, index)}
          onKeyUp={(e) => onKeyUp(e, index)}
          onKeyDown={(e) => onKeyDown(e)}
          onFocus={(e) => onFocus(e, index)}
        />
      ))}
    </>
  );
};
```

Finally, you can import this component into your App.tsx file like below:

```tsx
import "./styles.css";
import Passcode from "./components/Passcode";

export default function App() {
  return (
    <div className="App">
      <h1>Passcode component</h1>
      <Passcode />
    </div>
  );
}
```

Here is the working example of our Passcode component:

{% embed https://codesandbox.io/embed/otp-prototype-w5m4lk?fontsize=14&hidenavigation=1&theme=dark %}

## Summary
To wrap up, we have learned how to build a passcode component and solved some base case scenarios required for building this type of component.

If you would like to use this component, you can check out the library I made: [react-headless-passcode](https://github.com/keyurparalkar/react-headless-passcode). It's a headless library that allows you to pass the array value to the `usePasscode` hook, which takes care of all the complex scenarios mentioned above and other scenarios as well. With headless, you can focus more on building your passcode UI and styling it however you like. The entire control is given to the developer. The job of react-headless-passcode is to provide you with all the logic you need to get the passcode component up and running.

Thank you for reading!

Follow me on [Twitter](https://twitter.com/keurplkar), [GitHub](https://github.com/keyurparalkar), and [LinkedIn](https://www.linkedin.com/in/keyur-paralkar-494415107/).