Jul 17, 2024, 6:14 AM

How to create a 6 digit code authentication input box in React

Create a beautiful 6 digit code authentication input box in react by following these easy steps

Creating a six digit input box in react is easy with the steps below.


Firstly, we will create a function that accepts 4 props and return a JSX element for managing the code input


const CodeInput = ({ length = 6, onChange, loading }: { length?: number, loading?: boolean, onChange?: (v: string) => void }) => {
     .........
}


length: being the number of boxed,

onchange: take the value in the box

loading: disable the box when you are done editing


const CodeInput = ({ length = 6, onChange, loading }: { length?: number, loading?: boolean, onChange?: (v: string) => void }) => {
    const [values, setValues] = useState('');
    const [direction, setDirection] = useState(0);
    const inputRefs = useRef<HTMLInputElement[]>([])


    useEffect(() => {
      if (onChange) {
        onChange(values);
      }
    }, [values]);
 }


The useEffect tracks the changes to all the input box



  const handleKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
    const deletebuttons = ['Backspace', 'Delete'] as const
    const navbuttons = ['ArrowLeft', 'ArrowRight'] as const
    const key = e.key as any
    const value = ((e.target as any).value as string).trim();
    const name = Number((e.target as any).name)
    if (deletebuttons.includes(key)) {
      const pLgth = values.length ? values.length - 1 : 0;
      setValues(p => p.substring(0, pLgth));
      inputRefs.current[pLgth].focus()
    }
    if (navbuttons.includes(key)) {
      if (key == 'ArrowLeft') {
        setDirection(0)
        inputRefs?.current?.[name - 1]?.focus()
      } else {
        setDirection(1)
        if (value) inputRefs?.current?.[name + 1]?.focus()
      }
    }
  }


The handleKeyDown, as the name applies, handles key events, like back button, delete and arrow right or left


The final code for the function below:


import { ChangeEvent, KeyboardEvent, useEffect, useRef, useState } from "react";


const CodeInput = ({ length = 6, onChange, loading }: { length?: number, loading?: boolean, onChange?: (v: string) => void }) => {
  const [values, setValues] = useState('');
  const [direction, setDirection] = useState(0);
  const inputRefs = useRef<HTMLInputElement[]>([])


  useEffect(() => {
    if (onChange) {
      onChange(values);
    }
  }, [values]);


  const handleKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
    const deletebuttons = ['Backspace', 'Delete'] as const
    const navbuttons = ['ArrowLeft', 'ArrowRight'] as const
    const key = e.key as any
    const value = ((e.target as any).value as string).trim();
    const name = Number((e.target as any).name)
    if (deletebuttons.includes(key)) {
      const pLgth = values.length ? values.length - 1 : 0;
      setValues(p => p.substring(0, pLgth));
      inputRefs.current[pLgth].focus()
    }
    if (navbuttons.includes(key)) {
      if (key == 'ArrowLeft') {
        setDirection(0)
        inputRefs?.current?.[name - 1]?.focus()
      } else {
        setDirection(1)
        if (value) inputRefs?.current?.[name + 1]?.focus()
      }
    }
  }


  const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
    const value = (e.target.value[direction] || e.target.value[0])?.trim() as any
    if (!value || isNaN(value)) return
    const name = Number(e.target.name)
    setValues(p => {
      const v = p.split('');
      v[name] = value;
      return v.join('')
    })
    setDirection(0)
    if (name < length) inputRefs?.current?.[name + 1]?.focus()
  }


  return <div className="inline-block relative">
    {
      Array(length).fill(0).map((_, i) =>
        <input
          key={i}
          autoFocus={i == 0 ? true : false}
          onChange={handleChange}
          onKeyDown={handleKeyDown}
          ref={elem => { (inputRefs.current as any)[i] = elem }}
          required
          disabled={loading}
          value={values[i] || ''}
          placeholder="*"
          type="number"
          pattern="[0-9]+"
          name={`${i}`}
          className={`
        md:text-xl inline-flex mr-2 relative
        md:py-2 md:pl-4 w-[2.3rem] py-1 pl-3
      md:w-[3rem] focus:outline-1 
      placeholder:text-xl
      md:placeholder:text-3xl
      placeholder:absolute
      disabled:bg-slate-300
      justify-center items-center
      invalid:[&:not(:placeholder-shown):not(:focus)]:ring-2
    invalid:[&:not(:placeholder-shown):not(:focus)]:ring-red-200
    invalid:[&:not(:placeholder-shown):not(:focus)]:border-red-700 
      focus:ring-2 border rounded-md
      [-moz-appearance:_textfield] [&::-webkit-outer-spin-button]:m-0 [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:m-0 [&::-webkit-inner-spin-button]:appearance-none
      `} />
      )
    }
    <button type="submit" className="hidden w-0 h-0"></button>
  </div>;
};


export default CodeInput;