OTP Component issue
The cursor is not blinking when otp component is opened in each input otp section(the blinking animation is not there). Also i am not able to automatically focus with a blinking cursor on the first index of the input otp section.
In the shadcn documentation of input OTP component the tailwind.config.js needs to be modified like this for the blinking cursor animation: tailwind.config.js modification
Below is my code segment for tailwind.config.js:
/** @type {import('tailwindcss').Config} */
module.exports = {
darkMode: ["class"],
content: [
"./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
"./src/components/**/*.{js,ts,jsx,tsx,mdx}",
"./src/app/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {
keyframes: {
"caret-blink": {
"0%,70%,100%": { opacity: "1" },
"20%,50%": { opacity: "0" },
},
},
animation: {
"caret-blink": "caret-blink 1.25s ease-out infinite",
},
colors: {
lime: "#F4F8C9",
inputColor: "#E9F193",
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
primary: {
DEFAULT: "hsl(var(--primary))",
foreground: "hsl(var(--primary-foreground))",
},
card: {
DEFAULT: "hsl(var(--card))",
foreground: "hsl(var(--card-foreground))",
},
popover: {
DEFAULT: "hsl(var(--popover))",
foreground: "hsl(var(--popover-foreground))",
},
secondary: {
DEFAULT: "hsl(var(--secondary))",
foreground: "hsl(var(--secondary-foreground))",
},
muted: {
DEFAULT: "hsl(var(--muted))",
foreground: "hsl(var(--muted-foreground))",
},
accent: {
DEFAULT: "hsl(var(--accent))",
foreground: "hsl(var(--accent-foreground))",
},
destructive: {
DEFAULT: "hsl(var(--destructive))",
foreground: "hsl(var(--destructive-foreground))",
},
border: "hsl(var(--border))",
input: "hsl(var(--input))",
ring: "hsl(var(--ring))",
chart: {
1: "hsl(var(--chart-1))",
2: "hsl(var(--chart-2))",
3: "hsl(var(--chart-3))",
4: "hsl(var(--chart-4))",
5: "hsl(var(--chart-5))",
},
},
borderRadius: {
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)",
},
},
},
plugins: [require("tailwindcss-animate")],
};
Also, i had applied useRef hook to automatically focus on the first input otp section. That is happening which is in accordance with the below image:
focus input otp section
But, the cursor is not being shown nor i can type any number. I have to CLICK on the first index of the input otp section to write. I want this to focus as soon as mobile number is entered and otp component is displayed. Below is the code for my signUp.jsx component:
"use client";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import Link from "next/link";
import {
InputOTP,
InputOTPGroup,
InputOTPSeparator,
InputOTPSlot,
} from "@/components/ui/input-otp";
import { useState, useRef, useEffect } from "react";
const Signup = () => {
//! State for storing phoneNumber and OTP:
const [phoneNumber, setPhoneNumber] = useState("");
const [phoneNumberError, setPhoneNumberError] = useState(false);
const [showOtpInput, setShowOtpInput] = useState(false);
const [otp, setOtp] = useState("");
const [otpError, setOtpError] = useState("");
const [phoneErrorMessage, setPhoneErrorMessage] = useState("");
//! Ref to control the first OTP input slot
const firstOtpInputRef = useRef(null);
const handlePhoneNumber = (e) => {
setPhoneNumber(e.target.value);
//* Clear error messages when the user starts typing
setPhoneNumberError(false);
setPhoneErrorMessage("");
};
const handlePhoneSubmit = (e) => {
e.preventDefault();
//* phone validations
const regex = /[^0-9]/g;
if (regex.test(phoneNumber)) {
setPhoneNumberError(true);
setPhoneErrorMessage("Please enter numbers only!");
return;
}
if (phoneNumber.length !== 10) {
setPhoneNumberError(true);
setPhoneErrorMessage("Phone number should be 10 digits long only!");
return;
}
//* Call Backend API here...
//* show OTP field
setShowOtpInput(true);
console.log("Phone Number:", phoneNumber);
};
const handleOtpChange = (otp) => {
setOtp(otp);
//* Clear OTP error message when user types in OTP
if (otp.length >= 4) {
setOtpError("");
}
};
const handleOtpSubmit = (e) => {
e.preventDefault();
if (otp.length < 4) {
setOtpError("Please insert a 4-digit OTP!");
return;
}
const checkOtp = otp;
console.log("Final OTP:", checkOtp);
//* Call Backend API here to verify OTP...
console.log("Login Succesful!!");
setOtp(""); //* Reset OTP state to clear the input fields
};
useEffect(() => {
if (showOtpInput) {
if (firstOtpInputRef.current) {
console.log("Focusing on first OTP input slot...");
console.log(firstOtpInputRef.current); // Log the reference to see if it's not null or undefined
firstOtpInputRef.current.focus(); // Call focus() here
} else {
console.log("firstOtpInputRef.current is null or undefined");
}
}
}, [showOtpInput]);
return (
<>
<div className="grid grid-cols-2 justify-center bg-lime h-screen w-full">
<div className="flex flex-col items-start p-24">
<div className="space-y-2">
<h1 className="text-black text-4xl font-extrabold">Sign Up</h1>
{!showOtpInput && (
<p className="text-black">Please Enter Your Phone Number.</p>
)}
</div>
{/* //!Signup Form */}
{!showOtpInput || phoneNumberError ? (
<form className="w-full" onSubmit={handlePhoneSubmit}>
<div className="flex flex-col space-y-2 mt-5">
<p className="text-black font-extrabold">Phone Number</p>
<Input
type="tel"
className="text-black font-extrabold w-full bg-inputColor"
value={phoneNumber}
onChange={handlePhoneNumber}
></Input>
{phoneErrorMessage && (
<p className="text-red-500 font-extrabold">
{phoneErrorMessage}
</p>
)}
</div>
<div className="flex flex-col gap-4 justify-center items-center mt-5">
<Button type="submit" className="w-full h-12">
<p className="text-xl hover:font-extrabold hover:duration-300 duration-300">
Get OTP
</p>
</Button>
</div>
</form>
) : (
//! OTP Form Input
<form className="w-full" onSubmit={handleOtpSubmit}>
<div className="flex flex-col gap-4 justify-center items-center mt-5 ">
<p className="text-black">
OTP has been sent to{" "}
<span className="font-extrabold">+91 {phoneNumber}.</span>
</p>
<InputOTP maxLength={4} value={otp} onChange={handleOtpChange}>
<InputOTPGroup>
<InputOTPSlot
index={0}
ref = {firstOtpInputRef}
className="text-3xl h-16 w-16 border border-black text-black focus:border-black focus:ring-1 focus:ring-black font-extrabold"
/>
<InputOTPSlot
index={1}
className="text-3xl h-16 w-16 border border-black text-black focus:border-black focus:ring-1 focus:ring-black font-extrabold focus:outline-none"
/>
<InputOTPSlot
index={2}
className="text-3xl h-16 w-16 border border-black text-black focus:border-black focus:ring-1 focus:ring-black font-extrabold focus:outline-none"
/>
<InputOTPSlot
index={3}
className="text-3xl h-16 w-16 border border-black text-black focus:border-black focus:ring-1 focus:ring-black font-extrabold focus:outline-none"
/>
</InputOTPGroup>
</InputOTP>
{otpError && (
<div className="mt-2">
<p className="text-red-500 font-extrabold">{otpError}</p>
</div>
)}
<Button
type="submit"
variant="custom"
className="text-black w-full h-12"
>
<p className="text-xl text-black hover:font-extrabold hover:duration-300 duration-300">
Verify OTP
</p>
</Button>
</div>
</form>
)}
</div>
<div className="flex justify-center items-center text-black text-4xl font-extrabold">
Image Comes Here!
</div>
</div>
</>
);
};
export default Signup;
aviral kaushal is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.