1import {2 OTPField,3 OTPFieldGroup,4 OTPFieldInput,5 OTPFieldSeparator,6 OTPFieldSlot,7} from "~/components/ui/otp-field";8
9export default function OTPFieldDemo() {10 return (11 <OTPField maxLength={6}>12 <OTPFieldInput />13 <OTPFieldGroup>14 <OTPFieldSlot index={0} />15 <OTPFieldSlot index={1} />16 <OTPFieldSlot index={2} />17 </OTPFieldGroup>18 <OTPFieldSeparator />19 <OTPFieldGroup>20 <OTPFieldSlot index={3} />21 <OTPFieldSlot index={4} />22 <OTPFieldSlot index={5} />23 </OTPFieldGroup>24 </OTPField>25 );26}
npx shadcn@latest add https://solid-ui-neobrutalism.vercel.app/r/otp-field.json
yarn shadcn@latest add https://solid-ui-neobrutalism.vercel.app/r/otp-field.json
pnpm dlx shadcn@latest add https://solid-ui-neobrutalism.vercel.app/r/otp-field.json
bunx --bun shadcn@latest add https://solid-ui-neobrutalism.vercel.app/r/otp-field.json
Install the following dependencies
npm install @corvu/otp-field
yarn add @corvu/otp-field
pnpm add @corvu/otp-field
bun add @corvu/otp-field
Copy and paste the following code into your project
1import type { DynamicProps, RootProps } from "@corvu/otp-field";2import type { Component, ComponentProps, ValidComponent } from "solid-js";3
4import { Show, splitProps } from "solid-js";5import OtpField from "@corvu/otp-field";6import DotIcon from "lucide-solid/icons/dot";7
8import { cn } from "~/lib/utils";9
10export const REGEXP_ONLY_DIGITS = "^\\d*$";11export const REGEXP_ONLY_CHARS = "^[a-zA-Z]*$";12export const REGEXP_ONLY_DIGITS_AND_CHARS = "^[a-zA-Z0-9]*$";13
14type OTPFieldProps<T extends ValidComponent = "div"> = RootProps<T> & {15 class?: string;16};17
18const OTPField = <T extends ValidComponent = "div">(19 props: DynamicProps<T, OTPFieldProps<T>>,20) => {21 const [local, others] = splitProps(props as OTPFieldProps, ["class"]);22 return (23 <OtpField24 data-slot="otp-field"25 class={cn(26 "flex items-center gap-2 disabled:cursor-not-allowed has-[:disabled]:opacity-50",27 local.class,28 )}29 {...others}30 />31 );32};33
34const OTPFieldInput = OtpField.Input;35
36const OTPFieldGroup: Component<ComponentProps<"div">> = (props) => {37 const [local, others] = splitProps(props, ["class"]);38 return <div class={cn("flex items-center", local.class)} {...others} />;39};40
41const OTPFieldSlot: Component<ComponentProps<"div"> & { index: number }> = (42 props,43) => {44 const [local, others] = splitProps(props, ["class", "index"]);45 const context = OtpField.useContext();46 const char = () => context.value()[local.index];47 const showFakeCaret = () =>48 context.value().length === local.index && context.isInserting();49
50 return (51 <div52 data-slot="otp-field-slot"53 class={cn(54 "group relative flex size-10 items-center justify-center border-y border-r-2 border-border text-sm first:rounded-l-base first:border-l-2 last:rounded-r-base",55 local.class,56 )}57 {...others}58 >59 <div60 class={cn(61 "absolute inset-0 z-10 transition-all group-first:rounded-l-base group-last:rounded-r-base",62 context.activeSlots().includes(local.index) &&63 "ring-2 ring-primary ring-offset-background",64 )}65 />66 {char()}67 <Show when={showFakeCaret()}>68 <div class="pointer-events-none absolute inset-0 flex items-center justify-center">69 <div class="h-4 w-px animate-caret-blink bg-foreground duration-1000" />70 </div>71 </Show>72 </div>73 );74};75
76const OTPFieldSeparator: Component<ComponentProps<"div">> = (props) => {77 return (78 <div data-slot="otp-field-separator" {...props}>79 <DotIcon class="size-6" />80 </div>81 );82};83
84export {85 OTPField,86 OTPFieldInput,87 OTPFieldGroup,88 OTPFieldSlot,89 OTPFieldSeparator,90};
Update the import paths to match your project setup.
1import {2 OTPField,3 OTPFieldGroup,4 OTPFieldInput,5 OTPFieldSeparator,6 OTPFieldSlot,7} from "~/components/ui/otp-field";
1<OTPField maxLength={6}>2 <OTPFieldInput />3 <OTPFieldGroup>4 <OTPFieldSlot index={0} />5 <OTPFieldSlot index={1} />6 <OTPFieldSlot index={2} />7 </OTPFieldGroup>8 <OTPFieldSeparator />9 <OTPFieldGroup>10 <OTPFieldSlot index={3} />11 <OTPFieldSlot index={4} />12 <OTPFieldSlot index={5} />13 </OTPFieldGroup>14</OTPField>