본문으로 건너뛰기
언어

Input otpstandard

인증 번호처럼 정해진 자릿수를 칸으로 나눠 받는 일회용 비밀번호 입력(one-time password input, OTP)입니다. shadcn base-nova 의 `input-otp` 는 `input-otp` 패키지를 감쌉니다.

설치

pnpm
pnpm dlx shadcn@4.23.0 add @uikit/input-otp

Default

input-otp-example.tsx
"use client"
import * as React from "react"import { REGEXP_ONLY_DIGITS, REGEXP_ONLY_DIGITS_AND_CHARS } from "input-otp"
import {  Example,  ExampleWrapper,} from "@/components/example"import { Button } from "@/components/ui/button"import {  Card,  CardContent,  CardDescription,  CardFooter,  CardHeader,  CardTitle,} from "@/components/ui/card"import {  Field,  FieldDescription,  FieldError,  FieldLabel,} from "@/components/ui/field"import {  InputOTP,  InputOTPGroup,  InputOTPSeparator,  InputOTPSlot,} from "@/components/ui/input-otp"import { RefreshCwIcon } from "lucide-react"
export default function InputOTPExample() {  return (    <ExampleWrapper>      <InputOTPForm />      <InputOTPSimple />      <InputOTPPattern />      <InputOTPWithSeparator />      <InputOTPAlphanumeric />      <InputOTPDisabled />      <InputOTPFourDigits />      <InputOTPInvalid />    </ExampleWrapper>  )}
function InputOTPSimple() {  return (    <Example title="Simple">      <Field>        <FieldLabel htmlFor="simple">Simple</FieldLabel>        <InputOTP id="simple" maxLength={6}>          <InputOTPGroup>            <InputOTPSlot index={0} />            <InputOTPSlot index={1} />            <InputOTPSlot index={2} />          </InputOTPGroup>          <InputOTPSeparator />          <InputOTPGroup>            <InputOTPSlot index={3} />            <InputOTPSlot index={4} />            <InputOTPSlot index={5} />          </InputOTPGroup>        </InputOTP>      </Field>    </Example>  )}
function InputOTPPattern() {  return (    <Example title="Digits Only">      <Field>        <FieldLabel htmlFor="digits-only">Digits Only</FieldLabel>        <InputOTP id="digits-only" maxLength={6} pattern={REGEXP_ONLY_DIGITS}>          <InputOTPGroup>            <InputOTPSlot index={0} />            <InputOTPSlot index={1} />            <InputOTPSlot index={2} />            <InputOTPSlot index={3} />            <InputOTPSlot index={4} />            <InputOTPSlot index={5} />          </InputOTPGroup>        </InputOTP>      </Field>    </Example>  )}
function InputOTPWithSeparator() {  const [value, setValue] = React.useState("123456")
  return (    <Example title="With Separator">      <Field>        <FieldLabel htmlFor="with-separator">With Separator</FieldLabel>        <InputOTP          id="with-separator"          maxLength={6}          value={value}          onChange={setValue}        >          <InputOTPGroup>            <InputOTPSlot index={0} />            <InputOTPSlot index={1} />          </InputOTPGroup>          <InputOTPSeparator />          <InputOTPGroup>            <InputOTPSlot index={2} />            <InputOTPSlot index={3} />          </InputOTPGroup>          <InputOTPSeparator />          <InputOTPGroup>            <InputOTPSlot index={4} />            <InputOTPSlot index={5} />          </InputOTPGroup>        </InputOTP>      </Field>    </Example>  )}
function InputOTPAlphanumeric() {  return (    <Example title="Alphanumeric">      <Field>        <FieldLabel htmlFor="alphanumeric">Alphanumeric</FieldLabel>        <FieldDescription>Accepts both letters and numbers.</FieldDescription>        <InputOTP          id="alphanumeric"          maxLength={6}          pattern={REGEXP_ONLY_DIGITS_AND_CHARS}        >          <InputOTPGroup>            <InputOTPSlot index={0} />            <InputOTPSlot index={1} />            <InputOTPSlot index={2} />          </InputOTPGroup>          <InputOTPSeparator />          <InputOTPGroup>            <InputOTPSlot index={3} />            <InputOTPSlot index={4} />            <InputOTPSlot index={5} />          </InputOTPGroup>        </InputOTP>      </Field>    </Example>  )}
function InputOTPDisabled() {  return (    <Example title="Disabled">      <Field>        <FieldLabel htmlFor="disabled">Disabled</FieldLabel>        <InputOTP id="disabled" maxLength={6} disabled value="123456">          <InputOTPGroup>            <InputOTPSlot index={0} />            <InputOTPSlot index={1} />            <InputOTPSlot index={2} />          </InputOTPGroup>          <InputOTPSeparator />          <InputOTPGroup>            <InputOTPSlot index={3} />            <InputOTPSlot index={4} />            <InputOTPSlot index={5} />          </InputOTPGroup>        </InputOTP>      </Field>    </Example>  )}
function InputOTPFourDigits() {  return (    <Example title="4 Digits">      <Field>        <FieldLabel htmlFor="four-digits">4 Digits</FieldLabel>        <FieldDescription>Common pattern for PIN codes.</FieldDescription>        <InputOTP id="four-digits" maxLength={4} pattern={REGEXP_ONLY_DIGITS}>          <InputOTPGroup>            <InputOTPSlot index={0} />            <InputOTPSlot index={1} />            <InputOTPSlot index={2} />            <InputOTPSlot index={3} />          </InputOTPGroup>        </InputOTP>      </Field>    </Example>  )}
function InputOTPInvalid() {  const [value, setValue] = React.useState("000000")
  return (    <Example title="Invalid State">      <Field>        <FieldLabel htmlFor="invalid">Invalid State</FieldLabel>        <FieldDescription>          Example showing the invalid error state.        </FieldDescription>        <InputOTP id="invalid" maxLength={6} value={value} onChange={setValue}>          <InputOTPGroup>            <InputOTPSlot index={0} aria-invalid />            <InputOTPSlot index={1} aria-invalid />          </InputOTPGroup>          <InputOTPSeparator />          <InputOTPGroup>            <InputOTPSlot index={2} aria-invalid />            <InputOTPSlot index={3} aria-invalid />          </InputOTPGroup>          <InputOTPSeparator />          <InputOTPGroup>            <InputOTPSlot index={4} aria-invalid />            <InputOTPSlot index={5} aria-invalid />          </InputOTPGroup>        </InputOTP>        <FieldError errors={[{ message: "Invalid code. Please try again." }]} />      </Field>    </Example>  )}
function InputOTPForm() {  return (    <Example title="Form">      <Card className="mx-auto max-w-md">        <CardHeader>          <CardTitle>Verify your login</CardTitle>          <CardDescription>            Enter the verification code we sent to your email address:{" "}            <span className="font-medium">m@example.com</span>.          </CardDescription>        </CardHeader>        <CardContent>          <form>            <Field>              <div className="flex items-center justify-between">                <FieldLabel htmlFor="otp-verification">                  Verification code                </FieldLabel>                <Button variant="outline" size="xs">                  <RefreshCwIcon data-icon="inline-start" />                  Resend Code                </Button>              </div>              <InputOTP maxLength={6} id="otp-verification" required>                <InputOTPGroup className="*:data-[slot=input-otp-slot]:text-xl style-vega:*:data-[slot=input-otp-slot]:h-16 style-vega:*:data-[slot=input-otp-slot]:w-12 style-nova:*:data-[slot=input-otp-slot]:h-12 style-nova:*:data-[slot=input-otp-slot]:w-11 style-lyra:*:data-[slot=input-otp-slot]:h-12 style-lyra:*:data-[slot=input-otp-slot]:w-11 style-maia:*:data-[slot=input-otp-slot]:h-16 style-maia:*:data-[slot=input-otp-slot]:w-12 style-mira:*:data-[slot=input-otp-slot]:h-12 style-mira:*:data-[slot=input-otp-slot]:w-11 style-luma:*:data-[slot=input-otp-slot]:h-16 style-luma:*:data-[slot=input-otp-slot]:w-12">                  <InputOTPSlot index={0} />                  <InputOTPSlot index={1} />                  <InputOTPSlot index={2} />                </InputOTPGroup>                <InputOTPSeparator />                <InputOTPGroup className="*:data-[slot=input-otp-slot]:text-xl style-vega:*:data-[slot=input-otp-slot]:h-16 style-vega:*:data-[slot=input-otp-slot]:w-12 style-nova:*:data-[slot=input-otp-slot]:h-12 style-nova:*:data-[slot=input-otp-slot]:w-11 style-lyra:*:data-[slot=input-otp-slot]:h-12 style-lyra:*:data-[slot=input-otp-slot]:w-11 style-maia:*:data-[slot=input-otp-slot]:h-16 style-maia:*:data-[slot=input-otp-slot]:w-12 style-mira:*:data-[slot=input-otp-slot]:h-12 style-mira:*:data-[slot=input-otp-slot]:w-11 style-luma:*:data-[slot=input-otp-slot]:h-16 style-luma:*:data-[slot=input-otp-slot]:w-12">                  <InputOTPSlot index={3} />                  <InputOTPSlot index={4} />                  <InputOTPSlot index={5} />                </InputOTPGroup>              </InputOTP>              <FieldDescription>                <a href="#">I no longer have access to this email address.</a>              </FieldDescription>            </Field>          </form>        </CardContent>        <CardFooter className="flex-col gap-2">          <Button type="submit" className="w-full">            Verify          </Button>          <div className="text-sm text-muted-foreground">            Having trouble signing in?{" "}            <a              href="#"              className="underline underline-offset-4 transition-colors hover:text-primary"            >              Contact support            </a>          </div>        </CardFooter>      </Card>    </Example>  )}