Examples
Examples below use Tailwind utility classes for OTP-style layouts.
Basic
< div class = "flex max-w-md gap-2" data-kt-pin-input = "true" >
< input
type = "text"
maxlength = "1"
inputmode = "numeric"
autocomplete = "one-time-code"
class = "kt-input size-10 shrink-0 px-0 text-center text-sm"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "numeric"
autocomplete = "one-time-code"
class = "kt-input size-10 shrink-0 px-0 text-center text-sm"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "numeric"
autocomplete = "one-time-code"
class = "kt-input size-10 shrink-0 px-0 text-center text-sm"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "numeric"
autocomplete = "one-time-code"
class = "kt-input size-10 shrink-0 px-0 text-center text-sm"
data-kt-pin-input-item = "true"
/>
</ div >
Placeholder
< div class = "flex max-w-md gap-2" data-kt-pin-input = "true" >
< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "kt-input size-10 shrink-0 px-0 text-center text-sm"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "kt-input size-10 shrink-0 px-0 text-center text-sm"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "kt-input size-10 shrink-0 px-0 text-center text-sm"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "kt-input size-10 shrink-0 px-0 text-center text-sm"
data-kt-pin-input-item = "true"
/>
</ div >
Gray variant
< div class = "flex max-w-md gap-2" data-kt-pin-input = "true" >
< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "kt-input size-10 shrink-0 border-border/80 bg-muted/40 px-0 text-center text-sm focus:bg-background"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "kt-input size-10 shrink-0 border-border/80 bg-muted/40 px-0 text-center text-sm focus:bg-background"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "kt-input size-10 shrink-0 border-border/80 bg-muted/40 px-0 text-center text-sm focus:bg-background"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "kt-input size-10 shrink-0 border-border/80 bg-muted/40 px-0 text-center text-sm focus:bg-background"
data-kt-pin-input-item = "true"
/>
</ div >
Underline variant
< div class = "flex max-w-md gap-2" data-kt-pin-input = "true" >
< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "size-10 shrink-0 border-0 border-b-2 border-border bg-transparent px-0 text-center text-sm shadow-none focus-visible:border-primary focus-visible:ring-0"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "size-10 shrink-0 border-0 border-b-2 border-border bg-transparent px-0 text-center text-sm shadow-none focus-visible:border-primary focus-visible:ring-0"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "size-10 shrink-0 border-0 border-b-2 border-border bg-transparent px-0 text-center text-sm shadow-none focus-visible:border-primary focus-visible:ring-0"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "size-10 shrink-0 border-0 border-b-2 border-border bg-transparent px-0 text-center text-sm shadow-none focus-visible:border-primary focus-visible:ring-0"
data-kt-pin-input-item = "true"
/>
</ div >
Focus scale
< div class = "flex max-w-md gap-2" data-kt-pin-input = "true" >
< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "kt-input size-10 shrink-0 px-0 text-center text-sm transition-transform focus:scale-110"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "kt-input size-10 shrink-0 px-0 text-center text-sm transition-transform focus:scale-110"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "kt-input size-10 shrink-0 px-0 text-center text-sm transition-transform focus:scale-110"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "kt-input size-10 shrink-0 px-0 text-center text-sm transition-transform focus:scale-110"
data-kt-pin-input-item = "true"
/>
</ div >
Lengths (3, 5, 7)
< div class = "flex max-w-xl flex-col gap-4" >
< div class = "flex gap-2" data-kt-pin-input = "true" >
< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "kt-input size-10 shrink-0 px-0 text-center text-sm"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "kt-input size-10 shrink-0 px-0 text-center text-sm"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "kt-input size-10 shrink-0 px-0 text-center text-sm"
data-kt-pin-input-item = "true"
/>
</ div >
< div class = "flex gap-2" data-kt-pin-input = "true" >
< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "kt-input size-10 shrink-0 px-0 text-center text-sm"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "kt-input size-10 shrink-0 px-0 text-center text-sm"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "kt-input size-10 shrink-0 px-0 text-center text-sm"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "kt-input size-10 shrink-0 px-0 text-center text-sm"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "kt-input size-10 shrink-0 px-0 text-center text-sm"
data-kt-pin-input-item = "true"
/>
</ div >
< div class = "flex gap-2" data-kt-pin-input = "true" >
< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "kt-input size-10 shrink-0 px-0 text-center text-sm"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "kt-input size-10 shrink-0 px-0 text-center text-sm"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "kt-input size-10 shrink-0 px-0 text-center text-sm"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "kt-input size-10 shrink-0 px-0 text-center text-sm"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "kt-input size-10 shrink-0 px-0 text-center text-sm"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "kt-input size-10 shrink-0 px-0 text-center text-sm"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "kt-input size-10 shrink-0 px-0 text-center text-sm"
data-kt-pin-input-item = "true"
/>
</ div >
</ div >
Regex subset (digits 0–3)
< div
class = "flex max-w-md gap-2"
data-kt-pin-input = "true"
data-kt-pin-input-available-chars = "[0-3]"
>
< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "kt-input size-10 shrink-0 px-0 text-center text-sm"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "kt-input size-10 shrink-0 px-0 text-center text-sm"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "kt-input size-10 shrink-0 px-0 text-center text-sm"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "kt-input size-10 shrink-0 px-0 text-center text-sm"
data-kt-pin-input-item = "true"
/>
</ div >
Telephone number
Ten numeric cells with type="tel" and inputMode="numeric" so mobile browsers show the phone-style keypad . Paste distributes digits across cells.
< div class = "space-y-2" >
< p class = "text-muted-foreground text-start text-xs sm:text-sm" >
Enter a 10-digit number
</ p >
< div
class = "flex max-w-xl flex-wrap justify-start gap-2"
data-kt-pin-input = "true"
>
< input
type = "tel"
maxlength = "1"
inputmode = "numeric"
autocomplete = "off"
placeholder = "○"
aria-label = "Digit 1 of 10"
class = "kt-input size-9 shrink-0 px-0 text-center text-sm sm:size-10"
data-kt-pin-input-item = "true"
/>< input
type = "tel"
maxlength = "1"
inputmode = "numeric"
autocomplete = "off"
placeholder = "○"
aria-label = "Digit 2 of 10"
class = "kt-input size-9 shrink-0 px-0 text-center text-sm sm:size-10"
data-kt-pin-input-item = "true"
/>< input
type = "tel"
maxlength = "1"
inputmode = "numeric"
autocomplete = "off"
placeholder = "○"
aria-label = "Digit 3 of 10"
class = "kt-input size-9 shrink-0 px-0 text-center text-sm sm:size-10"
data-kt-pin-input-item = "true"
/>< input
type = "tel"
maxlength = "1"
inputmode = "numeric"
autocomplete = "off"
placeholder = "○"
aria-label = "Digit 4 of 10"
class = "kt-input size-9 shrink-0 px-0 text-center text-sm sm:size-10"
data-kt-pin-input-item = "true"
/>< input
type = "tel"
maxlength = "1"
inputmode = "numeric"
autocomplete = "off"
placeholder = "○"
aria-label = "Digit 5 of 10"
class = "kt-input size-9 shrink-0 px-0 text-center text-sm sm:size-10"
data-kt-pin-input-item = "true"
/>< input
type = "tel"
maxlength = "1"
inputmode = "numeric"
autocomplete = "off"
placeholder = "○"
aria-label = "Digit 6 of 10"
class = "kt-input size-9 shrink-0 px-0 text-center text-sm sm:size-10"
data-kt-pin-input-item = "true"
/>< input
type = "tel"
maxlength = "1"
inputmode = "numeric"
autocomplete = "off"
placeholder = "○"
aria-label = "Digit 7 of 10"
class = "kt-input size-9 shrink-0 px-0 text-center text-sm sm:size-10"
data-kt-pin-input-item = "true"
/>< input
type = "tel"
maxlength = "1"
inputmode = "numeric"
autocomplete = "off"
placeholder = "○"
aria-label = "Digit 8 of 10"
class = "kt-input size-9 shrink-0 px-0 text-center text-sm sm:size-10"
data-kt-pin-input-item = "true"
/>< input
type = "tel"
maxlength = "1"
inputmode = "numeric"
autocomplete = "off"
placeholder = "○"
aria-label = "Digit 9 of 10"
class = "kt-input size-9 shrink-0 px-0 text-center text-sm sm:size-10"
data-kt-pin-input-item = "true"
/>< input
type = "tel"
maxlength = "1"
inputmode = "numeric"
autocomplete = "off"
placeholder = "○"
aria-label = "Digit 10 of 10"
class = "kt-input size-9 shrink-0 px-0 text-center text-sm sm:size-10"
data-kt-pin-input-item = "true"
/>
</ div >
</ div >
Alphanumeric (10 characters)
Ten cells with data-kt-pin-input-available-chars="[0-9a-zA-Z]" and inputMode="text" for mixed codes (e.g. product keys). Letters are normalized to uppercase in this demo via CSS.
< div
class = "flex max-w-2xl flex-wrap justify-start gap-2"
data-kt-pin-input = "true"
data-kt-pin-input-available-chars = "[0-9a-zA-Z]"
>
< input
type = "text"
maxlength = "1"
inputmode = "text"
placeholder = "○"
aria-label = "Character 1 of 10"
class = "kt-input size-9 shrink-0 px-0 text-center text-sm uppercase sm:size-10"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "text"
placeholder = "○"
aria-label = "Character 2 of 10"
class = "kt-input size-9 shrink-0 px-0 text-center text-sm uppercase sm:size-10"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "text"
placeholder = "○"
aria-label = "Character 3 of 10"
class = "kt-input size-9 shrink-0 px-0 text-center text-sm uppercase sm:size-10"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "text"
placeholder = "○"
aria-label = "Character 4 of 10"
class = "kt-input size-9 shrink-0 px-0 text-center text-sm uppercase sm:size-10"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "text"
placeholder = "○"
aria-label = "Character 5 of 10"
class = "kt-input size-9 shrink-0 px-0 text-center text-sm uppercase sm:size-10"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "text"
placeholder = "○"
aria-label = "Character 6 of 10"
class = "kt-input size-9 shrink-0 px-0 text-center text-sm uppercase sm:size-10"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "text"
placeholder = "○"
aria-label = "Character 7 of 10"
class = "kt-input size-9 shrink-0 px-0 text-center text-sm uppercase sm:size-10"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "text"
placeholder = "○"
aria-label = "Character 8 of 10"
class = "kt-input size-9 shrink-0 px-0 text-center text-sm uppercase sm:size-10"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "text"
placeholder = "○"
aria-label = "Character 9 of 10"
class = "kt-input size-9 shrink-0 px-0 text-center text-sm uppercase sm:size-10"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "text"
placeholder = "○"
aria-label = "Character 10 of 10"
class = "kt-input size-9 shrink-0 px-0 text-center text-sm uppercase sm:size-10"
data-kt-pin-input-item = "true"
/>
</ div >
Masked (type="password")
< div class = "flex max-w-md gap-2" data-kt-pin-input = "true" >
< input
type = "password"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "kt-input size-10 shrink-0 px-0 text-center text-sm"
data-kt-pin-input-item = "true"
/>< input
type = "password"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "kt-input size-10 shrink-0 px-0 text-center text-sm"
data-kt-pin-input-item = "true"
/>< input
type = "password"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "kt-input size-10 shrink-0 px-0 text-center text-sm"
data-kt-pin-input-item = "true"
/>< input
type = "password"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "kt-input size-10 shrink-0 px-0 text-center text-sm"
data-kt-pin-input-item = "true"
/>
</ div >
iOS OTP hint
< div class = "flex max-w-md gap-2" data-kt-pin-input = "true" >
< input
type = "tel"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
autocomplete = "one-time-code"
class = "kt-input size-10 shrink-0 px-0 text-center text-sm"
data-kt-pin-input-item = "true"
/>< input
type = "tel"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
autocomplete = "off"
class = "kt-input size-10 shrink-0 px-0 text-center text-sm"
data-kt-pin-input-item = "true"
/>< input
type = "tel"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
autocomplete = "off"
class = "kt-input size-10 shrink-0 px-0 text-center text-sm"
data-kt-pin-input-item = "true"
/>< input
type = "tel"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
autocomplete = "off"
class = "kt-input size-10 shrink-0 px-0 text-center text-sm"
data-kt-pin-input-item = "true"
/>
</ div >
Disabled
< div class = "flex max-w-md gap-2" data-kt-pin-input = "true" >
< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
disabled = ""
class = "kt-input size-10 shrink-0 px-0 text-center text-sm"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
disabled = ""
class = "kt-input size-10 shrink-0 px-0 text-center text-sm"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
disabled = ""
class = "kt-input size-10 shrink-0 px-0 text-center text-sm"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
disabled = ""
class = "kt-input size-10 shrink-0 px-0 text-center text-sm"
data-kt-pin-input-item = "true"
/>
</ div >
Sizes
< div class = "flex max-w-xl flex-col gap-5" >
< div class = "flex gap-2" data-kt-pin-input = "true" >
< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "kt-input size-9 shrink-0 px-0 text-center text-xs"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "kt-input size-9 shrink-0 px-0 text-center text-xs"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "kt-input size-9 shrink-0 px-0 text-center text-xs"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "kt-input size-9 shrink-0 px-0 text-center text-xs"
data-kt-pin-input-item = "true"
/>
</ div >
< div class = "flex gap-2" data-kt-pin-input = "true" >
< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "kt-input size-10 shrink-0 px-0 text-center text-sm"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "kt-input size-10 shrink-0 px-0 text-center text-sm"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "kt-input size-10 shrink-0 px-0 text-center text-sm"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "kt-input size-10 shrink-0 px-0 text-center text-sm"
data-kt-pin-input-item = "true"
/>
</ div >
< div class = "flex gap-2" data-kt-pin-input = "true" >
< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "kt-input size-12 shrink-0 px-0 text-center text-base"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "kt-input size-12 shrink-0 px-0 text-center text-base"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "kt-input size-12 shrink-0 px-0 text-center text-base"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "kt-input size-12 shrink-0 px-0 text-center text-base"
data-kt-pin-input-item = "true"
/>
</ div >
</ div >
In modal
< div >
< button
type = "button"
class = "kt-btn kt-btn-primary kt-btn-sm"
data-kt-modal-toggle = "#ktui-pin-modal"
>
Open verification
</ button >
< div class = "kt-modal" data-kt-modal = "true" id = "ktui-pin-modal" >
< div class = "kt-modal-content max-w-[400px] top-[10%]" >
< div class = "kt-modal-header" >
< h3 class = "kt-modal-title" >Enter code</ h3 >
< button
type = "button"
class = "kt-modal-close"
aria-label = "Close modal"
data-kt-modal-dismiss = "#ktui-pin-modal"
>
< svg
xmlns = "http://www.w3.org/2000/svg"
width = "24"
height = "24"
viewBox = "0 0 24 24"
fill = "none"
stroke = "currentColor"
stroke-width = "2"
stroke-linecap = "round"
stroke-linejoin = "round"
class = "lucide lucide-x"
aria-hidden = "true"
>
< path d = "M18 6 6 18" ></ path >
< path d = "m6 6 12 12" ></ path >
</ svg >
</ button >
</ div >
< div class = "kt-modal-body flex justify-center py-2" >
< div class = "flex gap-2" data-kt-pin-input = "true" >
< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "kt-input size-11 shrink-0 px-0 text-center text-sm"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "kt-input size-11 shrink-0 px-0 text-center text-sm"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "kt-input size-11 shrink-0 px-0 text-center text-sm"
data-kt-pin-input-item = "true"
/>< input
type = "text"
maxlength = "1"
inputmode = "numeric"
placeholder = "○"
class = "kt-input size-11 shrink-0 px-0 text-center text-sm"
data-kt-pin-input-item = "true"
/>
</ div >
</ div >
< div class = "kt-modal-footer" >
< button
type = "button"
class = "kt-btn kt-btn-outline"
data-kt-modal-dismiss = "#ktui-pin-modal"
>
Close
</ button >
</ div >
</ div >
</ div >
</ div >
Keyboard and mobile keypads
KtUI does not draw a custom keyboard; behavior comes from native <input> hints plus the component’s focus and filtering logic.
Hint Effect inputMode="numeric"Mobile: numeric keypad (digits). Desktop: standard keyboard; the component enforces allowed characters. inputMode="text"Standard keyboard for alphanumeric patterns. type="tel"Use with inputMode="numeric" for phone numbers ; signals a telephony field to the platform.
Paste: Long strings are split and filtered; only characters matching the active pattern fill successive enabled cells.
Physical keyboard: Arrow keys move between cells; Home / End jump to the first / last enabled cell; Backspace clears or moves backward when empty.
Usage
Put data-kt-pin-input on a wrapper and mark each cell with data-kt-pin-input-item . Cells are native <input> elements (type="text", tel, or password as needed). The default allowed character class is digits ([0-9]); override with data-kt-pin-input-available-chars using a RegExp body that works with new RegExp(pattern).test(char) for each typed or pasted character.
< div data-kt-pin-input >
< input type = "text" inputmode = "numeric" data-kt-pin-input-item />
< input type = "text" inputmode = "numeric" data-kt-pin-input-item />
< input type = "text" inputmode = "numeric" data-kt-pin-input-item />
< input type = "text" inputmode = "numeric" data-kt-pin-input-item />
</ div >
Component API
Options
Name Type Default Description data-kt-pin-inputflag — Enables auto-init on the root wrapper. data-kt-pin-input-itemflag —
Disabled cells are skipped for focus navigation and paste distribution. Set maxlength="1" on cells if you want a hard browser cap (the component sets 1 when missing).
Methods
Method Description KTPinInput.init()Initializes all non-lazy roots in the document. KTPinInput.createInstances()Initializes newly inserted roots. KTPinInput.getInstance(root)Returns the instance or null.
import {
KTPinInput,
type KTPinInputConfigInterface,
type KTPinInputInterface,
} from '@keenthemes/ktui' ;
const el = document. querySelector < HTMLElement >( '[data-kt-pin-input]' );
if ( ! el) throw new Error ( 'Missing pin root' );
const pin : KTPinInputInterface | null = KTPinInput. getOrCreateInstance (el, {
availableChars: '[0-9]' ,
} satisfies KTPinInputConfigInterface );
pin?. setValue ( '' );
console. log (pin?.
Events
Events bubble from the root element. Payload is in event.detail.payload.
Event Description kt.pin-input.inputFired when the combined value changes. kt.pin-input.changeSame timing as input in v1 (mirrors common “value updated” hooks). kt.pin-input.completeFired once when all enabled cells become non-empty (not repeated until incomplete again).
Payload fields:
Field Description valueFull string from cells. completetrue when every enabled cell has a character.cellCountNumber of enabled cells.
const root = document. querySelector ( '[data-kt-pin-input]' );
root?. addEventListener ( 'kt.pin-input.complete' , ( e ) => {
console. log ( 'OTP complete:' , e.detail.payload.value);
});
Accessibility
Keep visible labels or aria-label / aria-labelledby on the group; consider role="group" with a single label for the set (markup depends on your form layout).
For SMS OTP on iOS, use autocomplete="one-time-code" on the first cell and type="tel" with inputmode="numeric" (see iOS OTP example).
Arrow keys move between cells; Home / End jump to first / last enabled cell.