Understanding and Implementing One-Time Password (OTP) Input Component in React
Tags: opensource, typescript, reactjsdevelopment, webdev
Published: 2023-07-18 02:17:24

## Introduction

Do you find yourself curious about how the one-time password (OTP) input component operates on an application or webpage? This OTP feature, composed of several input elements, each accepting only one character, behaves as a cohesive unit, akin to a normal input component. In this blog post, we will explain how to create this OTP component, understand its workings, and delve into the various use cases involved with it. Let's dive into it right away!

## Understanding the OTP Component

The OTP or passcode component is essentially a collection of individual input boxes where each box corresponds to a single character of the passcode. This has become a prevalent practice in modern web and mobile applications as part of the validation process. However, implementing this feature is not as straightforward as it seems. It requires handling a variety of scenarios, including key event handling, providing a user-friendly pasting experience, and managing focus shifts in a sensible manner.

## Key Scenarios in a Passcode Component

When designing the passcode component, it's crucial to consider the following scenarios:

> View a live view on [vercel](https://react-next-passcode.vercel.app)

### Fundamental scenarios:

Each input box should accept only a single character.
Upon character entry, the focus should automatically shift to the next input element.
When backspacing, the focus should shift from the current to the previous input element.
Advanced scenarios:

Pasting Experience: This includes handling user permissions, determining where the focus should land after pasting, and managing how the input fields are populated when a value is pasted.
Diving into the Code Implementation
Now that we've explored the key scenarios, let's delve into how to implement them. Below is a code snippet demonstrating a more advanced version of the OTP component:

### Advanced scenarios:

- Pasting Experience: This includes handling user permissions, determining where the focus should land after pasting, and managing how the input fields are populated when a value is pasted.

## Diving into the Code Implementation
Now that we've explored the key scenarios, let's delve into how to implement them. Below is a code snippet demonstrating a more advanced version of the OTP component:

[Github Repo](https://github.com/dirheimerb/react-next-passcode.git)

State Variables:

```ts
const [arrayValue, setArrayValue] = useState<(string | number)[]>(['', '', '', '']);
const [maskedValue, setMaskedValue] = useState<(string | number)[]>(['', '', '', '']);
const [showTooltip, setShowTooltip] = useState(false);
const inputRefs = useRef<(HTMLInputElement | null)[]>([]);
```

The component maintains four separate pieces of state:

arrayValue is the actual value of the four-digit code being entered.
maskedValue is a display-only value which replaces the numbers in arrayValue with asterisks.
showTooltip is a boolean value that toggles the display of a tooltip.
inputRefs holds references to the four input elements.
Page load focus:

```ts
React.useEffect(() => {
    inputRefs.current[0]?.focus();
}, []);
```

This useEffect hook is triggered on initial render, and it sets the focus to the first input field.

Paste function:

```ts
Copy code
const onPaste = (e: React.ClipboardEvent, index: number) => { ... };
```

This function handles pasting from the clipboard. It prevents the default paste action, and instead splits the pasted text into an array. If every character in the array is a number, it updates arrayValue and maskedValue with the pasted values.


`Key down function:`

```ts

const onKeyDown = (e: KeyboardEvent<HTMLInputElement>) => { ... };
```

This function handles key down events. It prevents the default action for every key that isn't Backspace, Delete, Tab, or a number between 0 and 9. If one of the forbidden keys is pressed, it displays a tooltip for two seconds.

`Change function:`

```ts
const onChange = (e: BaseSyntheticEvent, index: number) => { ... };
```

This function is called whenever the user types into an input. If the input is a number, it updates arrayValue and maskedValue. If the input is not empty, it moves the focus to the next input.

`Key up function:`

```ts
const onKeyUp = (e: KeyboardEvent<HTMLInputElement>, index: number) => { ... };
```

This function is called when a key is released. If Backspace or Delete is released, it clears the current input and moves the focus to the previous input.

`Reset function:`

```ts
const resetInputs = () => { ... };
```

This function resets all inputs and refocuses on the first input.

`Submit function:`

```ts
const handleSubmit = () => { ... };
```
This function handles the form submission. Right now, it simply logs the entered value. You would typically add your database logic here.


{% embed https://codesandbox.io/p/github/dirheimerb/react-next-passcode/draft/nervous-fog?layout=%257B%2522sidebarPanel%2522%253A%2522EXPLORER%2522%252C%2522rootPanelGroup%2522%253A%257B%2522direction%2522%253A%2522vertical%2522%252C%2522contentType%2522%253A%2522UNKNOWN%2522%252C%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522id%2522%253A%2522ROOT_LAYOUT%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522EDITOR%2522%252C%2522direction%2522%253A%2522horizontal%2522%252C%2522id%2522%253A%2522EDITOR%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL%2522%252C%2522contentType%2522%253A%2522EDITOR%2522%252C%2522id%2522%253A%2522clk7nwfu605tp3b6m38clqgkm%2522%257D%255D%252C%2522sizes%2522%253A%255B100%255D%257D%252C%257B%2522type%2522%253A%2522PANEL_GROUP%2522%252C%2522contentType%2522%253A%2522DEVTOOLS%2522%252C%2522direction%2522%253A%2522horizontal%2522%252C%2522id%2522%253A%2522DEVTOOLS%2522%252C%2522panels%2522%253A%255B%257B%2522type%2522%253A%2522PANEL%2522%252C%2522contentType%2522%253A%2522DEVTOOLS%2522%252C%2522id%2522%253A%2522clk7nwfu605tq3b6m5raffk76%2522%257D%255D%252C%2522sizes%2522%253A%255B100%255D%257D%255D%252C%2522sizes%2522%253A%255B50%252C50%255D%257D%252C%2522tabbedPanels%2522%253A%257B%2522clk7nwfu605tp3b6m38clqgkm%2522%253A%257B%2522tabs%2522%253A%255B%257B%2522id%2522%253A%2522clk7nvzl105ol3b6m5u9gn1ne%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522type%2522%253A%2522FILE%2522%252C%2522filepath%2522%253A%2522%252FREADME.md%2522%252C%2522state%2522%253A%2522IDLE%2522%257D%255D%252C%2522id%2522%253A%2522clk7nwfu605tp3b6m38clqgkm%2522%252C%2522activeTabId%2522%253A%2522clk7nvzl105ol3b6m5u9gn1ne%2522%257D%252C%2522clk7nwfu605tq3b6m5raffk76%2522%253A%257B%2522tabs%2522%253A%255B%257B%2522type%2522%253A%2522UNASSIGNED_PORT%2522%252C%2522port%2522%253A3000%252C%2522id%2522%253A%2522clk7ngaox02lp3b6m6qdumg64%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522path%2522%253A%2522%252F%2522%257D%252C%257B%2522id%2522%253A%2522clk7nhs9z03te3b6mww3r0gi5%2522%252C%2522mode%2522%253A%2522permanent%2522%252C%2522type%2522%253A%2522UNASSIGNED_PORT%2522%252C%2522port%2522%253A3000%252C%2522path%2522%253A%2522%252F%2522%257D%252C%257B%2522type%2522%253A%2522TASK_LOG%2522%252C%2522taskId%2522%253A%2522dev%2522%252C%2522id%2522%253A%2522clk7lence01ff3b6m20w3ltyj%2522%252C%2522mode%2522%253A%2522permanent%2522%257D%255D%252C%2522id%2522%253A%2522clk7nwfu605tq3b6m5raffk76%2522%252C%2522activeTabId%2522%253A%2522clk7ngaox02lp3b6m6qdumg64%2522%257D%257D%252C%2522showDevtools%2522%253Atrue%252C%2522showShells%2522%253Afalse%252C%2522showSidebar%2522%253Afalse%252C%2522sidebarPanelSize%2522%253A15%257D %}