ورودی رمز یکبار مصرف (Input OTP)

کامپوننت ورودی رمز یکبار مصرف با قابلیت کپی و پیست و پشتیبانی کامل از RTL و دسترسی‌پذیری.

نصب (Installation)

1npx @quark-lab/rad-ui add input-otp

نمونه‌ها (Examples)

استفاده پایه (Basic Usage)

استفاده ساده از ورودی OTP با ۶ رقم.

مشاهده کد
1import {
2  InputOTP,
3  InputOTPGroup,
4  InputOTPSlot,
5  InputOTPSeparator,
6} from "@/components/ui/input-otp";
7
8export default function BasicExample() {
9  return (
10    <InputOTP maxLength={6}>
11      <InputOTPGroup>
12        <InputOTPSlot index={0} />
13        <InputOTPSlot index={1} />
14        <InputOTPSlot index={2} />
15      </InputOTPGroup>
16      <InputOTPSeparator />
17      <InputOTPGroup>
18        <InputOTPSlot index={3} />
19        <InputOTPSlot index={4} />
20        <InputOTPSlot index={5} />
21      </InputOTPGroup>
22    </InputOTP>
23  );
24}
25

فقط اعداد (Digits Only)

محدود کردن ورودی به اعداد با استفاده از الگو.

مشاهده کد
1import {
2  InputOTP,
3  InputOTPGroup,
4  InputOTPSlot,
5  InputOTPSeparator,
6  REGEXP_ONLY_DIGITS,
7} from "@/components/ui/input-otp";
8import { Label } from "@/components/ui/label";
9
10export default function PatternExample() {
11  return (
12    <div className="space-y-2 flex flex-col items-center">
13      <Label>رمز عددی</Label>
14      <InputOTP maxLength={6} pattern={REGEXP_ONLY_DIGITS}>
15        <InputOTPGroup>
16          <InputOTPSlot index={0} />
17          <InputOTPSlot index={1} />
18          <InputOTPSlot index={2} />
19        </InputOTPGroup>
20        <InputOTPSeparator />
21        <InputOTPGroup>
22          <InputOTPSlot index={3} />
23          <InputOTPSlot index={4} />
24          <InputOTPSlot index={5} />
25        </InputOTPGroup>
26      </InputOTP>
27    </div>
28  );
29}
30

جداکننده (Separator)

افزودن جداکننده بین گروه‌های ورودی.

مشاهده کد
1import {
2  InputOTP,
3  InputOTPGroup,
4  InputOTPSlot,
5  InputOTPSeparator,
6} from "@/components/ui/input-otp";
7
8export default function SeparatorExample() {
9  return (
10    <InputOTP maxLength={4}>
11      <InputOTPGroup>
12        <InputOTPSlot index={0} />
13        <InputOTPSlot index={1} />
14      </InputOTPGroup>
15      <InputOTPSeparator />
16      <InputOTPGroup>
17        <InputOTPSlot index={2} />
18        <InputOTPSlot index={3} />
19      </InputOTPGroup>
20    </InputOTP>
21  );
22}
23

غیرفعال (Disabled)

غیرفعال کردن ورودی.

مشاهده کد
1import {
2  InputOTP,
3  InputOTPGroup,
4  InputOTPSlot,
5  InputOTPSeparator,
6} from "@/components/ui/input-otp";
7
8export default function DisabledExample() {
9  return (
10    <InputOTP maxLength={6} disabled>
11      <InputOTPGroup>
12        <InputOTPSlot index={0} />
13        <InputOTPSlot index={1} />
14        <InputOTPSlot index={2} />
15      </InputOTPGroup>
16      <InputOTPSeparator />
17      <InputOTPGroup>
18        <InputOTPSlot index={3} />
19        <InputOTPSlot index={4} />
20        <InputOTPSlot index={5} />
21      </InputOTPGroup>
22    </InputOTP>
23  );
24}
25

کنترل‌شده (Controlled)

کنترل مقدار ورودی و نمایش آن.

رمز یکبار مصرف خود را وارد کنید.

مشاهده کد
1import { useState } from "react";
2import {
3  InputOTP,
4  InputOTPGroup,
5  InputOTPSlot,
6  InputOTPSeparator,
7} from "@/components/ui/input-otp";
8
9const PERSIAN_DIGITS = ["۰", "۱", "۲", "۳", "۴", "۵", "۶", "۷", "۸", "۹"];
10function toPersianDigits(str: string): string {
11  return str.replace(/\d/g, (d) => PERSIAN_DIGITS[parseInt(d)] ?? d);
12}
13
14export default function ControlledExample() {
15  const [value, setValue] = useState("");
16
17  return (
18    <div className="flex flex-col items-center gap-4">
19      <InputOTP
20        maxLength={6}
21        value={value}
22        onChange={setValue}
23      >
24        <InputOTPGroup>
25          <InputOTPSlot index={0} />
26          <InputOTPSlot index={1} />
27          <InputOTPSlot index={2} />
28        </InputOTPGroup>
29        <InputOTPSeparator />
30        <InputOTPGroup>
31          <InputOTPSlot index={3} />
32          <InputOTPSlot index={4} />
33          <InputOTPSlot index={5} />
34        </InputOTPGroup>
35      </InputOTP>
36      <p className="text-sm text-muted-foreground text-center">
37        {value === "" ? (
38          <>رمز یکبار مصرف خود را وارد کنید.</>
39        ) : (
40          <>
41            مقدار وارد شده:{" "}
42            <span className="font-mono font-bold" dir="ltr">
43              {toPersianDigits(value)}
44            </span>
45          </>
46        )}
47      </p>
48    </div>
49  );
50}
51

نامعتبر (Invalid)

نمایش حالت خطا برای ورودی.

۰
۰
۰
۰
۰
۰

رمز وارد شده نامعتبر است. لطفاً دوباره تلاش کنید.

مشاهده کد
1import {
2  InputOTP,
3  InputOTPGroup,
4  InputOTPSlot,
5  InputOTPSeparator,
6} from "@/components/ui/input-otp";
7
8export default function InvalidExample() {
9  return (
10    <div className="flex flex-col items-center gap-3">
11      <InputOTP maxLength={6} defaultValue="000000">
12        <InputOTPGroup>
13          <InputOTPSlot
14            index={0}
15            className="border-destructive text-destructive"
16          />
17          <InputOTPSlot
18            index={1}
19            className="border-destructive text-destructive"
20          />
21          <InputOTPSlot
22            index={2}
23            className="border-destructive text-destructive"
24          />
25        </InputOTPGroup>
26        <InputOTPSeparator />
27        <InputOTPGroup>
28          <InputOTPSlot
29            index={3}
30            className="border-destructive text-destructive"
31          />
32          <InputOTPSlot
33            index={4}
34            className="border-destructive text-destructive"
35          />
36          <InputOTPSlot
37            index={5}
38            className="border-destructive text-destructive"
39          />
40        </InputOTPGroup>
41      </InputOTP>
42      <p className="text-sm text-destructive">
43        رمز وارد شده نامعتبر است. لطفاً دوباره تلاش کنید.
44      </p>
45    </div>
46  );
47}
48

چهار رقمی (Four Digits)

الگوی رایج برای کد PIN چهار رقمی.

مشاهده کد
1import {
2  InputOTP,
3  InputOTPGroup,
4  InputOTPSlot,
5  REGEXP_ONLY_DIGITS,
6} from "@/components/ui/input-otp";
7import { Label } from "@/components/ui/label";
8
9export default function FourDigitsExample() {
10  return (
11    <div className="space-y-2 flex flex-col items-center">
12      <Label>کد PIN</Label>
13      <InputOTP maxLength={4} pattern={REGEXP_ONLY_DIGITS}>
14        <InputOTPGroup>
15          <InputOTPSlot index={0} />
16          <InputOTPSlot index={1} />
17          <InputOTPSlot index={2} />
18          <InputOTPSlot index={3} />
19        </InputOTPGroup>
20      </InputOTP>
21    </div>
22  );
23}
24

حروف و اعداد (Alphanumeric)

پذیرش حروف و اعداد در ورودی.

مشاهده کد
1import {
2  InputOTP,
3  InputOTPGroup,
4  InputOTPSlot,
5  InputOTPSeparator,
6  REGEXP_ONLY_DIGITS_AND_CHARS,
7} from "@/components/ui/input-otp";
8
9export default function AlphanumericExample() {
10  return (
11    <InputOTP maxLength={6} pattern={REGEXP_ONLY_DIGITS_AND_CHARS}>
12      <InputOTPGroup>
13        <InputOTPSlot index={0} />
14        <InputOTPSlot index={1} />
15        <InputOTPSlot index={2} />
16      </InputOTPGroup>
17      <InputOTPSeparator />
18      <InputOTPGroup>
19        <InputOTPSlot index={3} />
20        <InputOTPSlot index={4} />
21        <InputOTPSlot index={5} />
22      </InputOTPGroup>
23    </InputOTP>
24  );
25}
26

نمونه کاربردی (Practical Example)

فرم تأیید (Verification Form)

نمونه کامل فرم تأیید رمز یکبار مصرف.

تأیید ورود

کد تأیید ارسال شده به ایمیل m@example.com را وارد کنید.

کد ارسال نشد؟

مشکلی در ورود دارید؟ تماس با پشتیبانی

مشاهده کد
1import { useState } from "react";
2import {
3  InputOTP,
4  InputOTPGroup,
5  InputOTPSlot,
6  InputOTPSeparator,
7} from "@/components/ui/input-otp";
8import { Label } from "@/components/ui/label";
9import { Button } from "@/components/ui/button";
10
11export default function FormExample() {
12  const [formValue, setFormValue] = useState("");
13  const [formSubmitted, setFormSubmitted] = useState(false);
14
15  return (
16    <div className="max-w-md mx-auto">
17      <div className="space-y-6">
18        <div className="text-center space-y-2">
19          <h3 className="text-xl font-semibold">تأیید ورود</h3>
20          <p className="text-sm text-muted-foreground">
21            کد تأیید ارسال شده به ایمیل{" "}
22            <span className="font-medium text-foreground" dir="ltr">
23              m@example.com
24            </span>{" "}
25            را وارد کنید.
26          </p>
27        </div>
28
29        <form
30          onSubmit={(e) => {
31            e.preventDefault();
32            setFormSubmitted(true);
33          }}
34          className="space-y-6"
35        >
36          <div className="space-y-2">
37            <Label htmlFor="otp-form">کد تأیید</Label>
38            <div className="flex justify-center">
39              <InputOTP
40                id="otp-form"
41                maxLength={6}
42                value={formValue}
43                onChange={setFormValue}
44              >
45                <InputOTPGroup>
46                  <InputOTPSlot index={0} />
47                  <InputOTPSlot index={1} />
48                  <InputOTPSlot index={2} />
49                </InputOTPGroup>
50                <InputOTPSeparator />
51                <InputOTPGroup>
52                  <InputOTPSlot index={3} />
53                  <InputOTPSlot index={4} />
54                  <InputOTPSlot index={5} />
55                </InputOTPGroup>
56              </InputOTP>
57            </div>
58            <p className="text-xs text-muted-foreground text-center">
59              کد ارسال نشد؟{" "}
60              <button
61                type="button"
62                className="text-primary underline hover:text-primary/80"
63              >
64                ارسال مجدد
65              </button>
66            </p>
67          </div>
68
69          <Button
70            type="submit"
71            className="w-full"
72            disabled={formValue.length < 6}
73          >
74            تأیید
75          </Button>
76
77          {formSubmitted && formValue.length === 6 && (
78            <p className="text-sm text-center text-primary">
79              کد با موفقیت ارسال شد!
80            </p>
81          )}
82        </form>
83
84        <p className="text-xs text-center text-muted-foreground">
85          مشکلی در ورود دارید؟{" "}
86          <a href="#" className="text-primary underline">
87            تماس با پشتیبانی
88          </a>
89        </p>
90      </div>
91    </div>
92  );
93}
94

پشتیبانی RTL

کامپوننت Input OTP به طور کامل از جهت‌گیری راست‌چین پشتیبانی می‌کند. از آنجا که ورود ارقام همیشه از چپ به راست است، ورودی خود دارای جهت dir="ltr" است اما برچسب‌ها و توضیحات اطراف آن از جهت سند پیروی می‌کنند.

  • حاشیه‌های اسلات‌ها از ویژگی‌های منطقی CSS استفاده می‌کنند (border-inline-start، border-inline-end)
  • گوشه‌های گرد از rounded-s-md و rounded-e-md استفاده می‌کنند
  • برچسب‌ها و متون راهنما با RTL هماهنگ هستند

مرجع API (API Reference)

InputOTP

کامپوننت اصلی ورودی رمز یکبار مصرف.

پراپ (Prop)نوع (Type)پیش‌فرض (Default)توضیحات (Description)
maxLengthnumber-حداکثر تعداد کاراکترهای ورودی (اجباری)
patternstring | RegExp-الگوی مجاز برای ورودی
valuestring-مقدار کنترل‌شده ورودی
onChange(value: string) => void-تابع فراخوانی هنگام تغییر مقدار
disabledbooleanfalseغیرفعال کردن ورودی
containerClassNamestring-کلاس‌های CSS برای محفظه بیرونی
classNamestring-کلاس‌های CSS سفارشی

InputOTPSlot

کامپوننت نمایش هر رقم یا کاراکتر.

پراپ (Prop)نوع (Type)پیش‌فرض (Default)توضیحات (Description)
indexnumber-شماره اندیس اسلات در ورودی OTP (اجباری)
classNamestring-کلاس‌های CSS سفارشی

ثابت‌های الگو

ثابت (Constant)توضیحات
REGEXP_ONLY_DIGITSفقط ارقام (۰ تا ۹)
REGEXP_ONLY_DIGITS_AND_CHARSارقام و حروف انگلیسی