package examples import "github.com/axadrn/shadcn-templ/v2/components/inputotp"package examples import "github.com/axadrn/shadcn-templ/v2/components/inputotp" templ InputOTPDemo() { @inputotp.InputOTP(inputotp.Props{Value: "123456"}) { @inputotp.Group() { @inputotp.Slot(inputotp.SlotProps{Index: 0}) @inputotp.Slot(inputotp.SlotProps{Index: 1}) @inputotp.Slot(inputotp.SlotProps{Index: 2}) @inputotp.Slot(inputotp.SlotProps{Index: 3}) @inputotp.Slot(inputotp.SlotProps{Index: 4}) @inputotp.Slot(inputotp.SlotProps{Index: 5}) } }}About
The InputOTP component is a native templ and vanilla JavaScript implementation with no external dependencies.
Installation
Usage
Composition
Use the following composition to build an InputOTP:
Pattern
Use the Pattern prop to define a custom pattern for the OTP input.
package examples import (package examples import ( "github.com/axadrn/shadcn-templ/v2/components/field" "github.com/axadrn/shadcn-templ/v2/components/inputotp") templ InputOTPPattern() { @field.Field(field.Props{Class: "w-fit"}) { @field.Label(field.LabelProps{For: "digits-only"}) { Digits Only } @inputotp.InputOTP(inputotp.Props{ID: "digits-only", Pattern: inputotp.RegexpOnlyDigits}) { @inputotp.Group() { @inputotp.Slot(inputotp.SlotProps{Index: 0}) @inputotp.Slot(inputotp.SlotProps{Index: 1}) @inputotp.Slot(inputotp.SlotProps{Index: 2}) @inputotp.Slot(inputotp.SlotProps{Index: 3}) @inputotp.Slot(inputotp.SlotProps{Index: 4}) @inputotp.Slot(inputotp.SlotProps{Index: 5}) } } }}Separator
Use the inputotp.Separator component to add a separator between input groups.
package examples import "github.com/axadrn/shadcn-templ/v2/components/inputotp"package examples import "github.com/axadrn/shadcn-templ/v2/components/inputotp" templ InputOTPSeparatorExample() { @inputotp.InputOTP() { @inputotp.Group() { @inputotp.Slot(inputotp.SlotProps{Index: 0}) @inputotp.Slot(inputotp.SlotProps{Index: 1}) } @inputotp.Separator() @inputotp.Group() { @inputotp.Slot(inputotp.SlotProps{Index: 2}) @inputotp.Slot(inputotp.SlotProps{Index: 3}) } @inputotp.Separator() @inputotp.Group() { @inputotp.Slot(inputotp.SlotProps{Index: 4}) @inputotp.Slot(inputotp.SlotProps{Index: 5}) } }}Disabled
Use the Disabled prop to disable the input.
package examples import "github.com/axadrn/shadcn-templ/v2/components/inputotp"package examples import "github.com/axadrn/shadcn-templ/v2/components/inputotp" templ InputOTPDisabled() { @inputotp.InputOTP(inputotp.Props{ID: "disabled", Disabled: true, Value: "123456"}) { @inputotp.Group() { @inputotp.Slot(inputotp.SlotProps{Index: 0}) @inputotp.Slot(inputotp.SlotProps{Index: 1}) @inputotp.Slot(inputotp.SlotProps{Index: 2}) } @inputotp.Separator() @inputotp.Group() { @inputotp.Slot(inputotp.SlotProps{Index: 3}) @inputotp.Slot(inputotp.SlotProps{Index: 4}) @inputotp.Slot(inputotp.SlotProps{Index: 5}) } }}Controlled
Use the Value prop and the hidden input’s change event to control the input value.
package examples import "github.com/axadrn/shadcn-templ/v2/components/inputotp"package examples import "github.com/axadrn/shadcn-templ/v2/components/inputotp" // The vanilla pendant of the demo's React state, the real input's input// event carries the combined value.templ InputOTPControlled() { <div class="space-y-2" data-otp-controlled-demo> @inputotp.InputOTP() { @inputotp.Group() { @inputotp.Slot(inputotp.SlotProps{Index: 0}) @inputotp.Slot(inputotp.SlotProps{Index: 1}) @inputotp.Slot(inputotp.SlotProps{Index: 2}) @inputotp.Slot(inputotp.SlotProps{Index: 3}) @inputotp.Slot(inputotp.SlotProps{Index: 4}) @inputotp.Slot(inputotp.SlotProps{Index: 5}) } } <div class="text-center text-sm" data-otp-status>Enter your one-time password.</div> </div> <script nonce={ templ.GetNonce(ctx) }> document.addEventListener('input', (e) => { const demo = e.target.closest?.('[data-otp-controlled-demo]'); if (!demo || !e.target.hasAttribute('data-tui-inputotp-input')) return; const value = e.target.value; demo.querySelector('[data-otp-status]').textContent = value === '' ? 'Enter your one-time password.' : 'You entered: ' + value; }); </script>}Invalid
Use aria-invalid on the slots to show an error state.
package examples import "github.com/axadrn/shadcn-templ/v2/components/inputotp"package examples import "github.com/axadrn/shadcn-templ/v2/components/inputotp" templ InputOTPInvalid() { @inputotp.InputOTP(inputotp.Props{Value: "000000"}) { @inputotp.Group() { @inputotp.Slot(inputotp.SlotProps{Index: 0, Attributes: templ.Attributes{"aria-invalid": "true"}}) @inputotp.Slot(inputotp.SlotProps{Index: 1, Attributes: templ.Attributes{"aria-invalid": "true"}}) } @inputotp.Separator() @inputotp.Group() { @inputotp.Slot(inputotp.SlotProps{Index: 2, Attributes: templ.Attributes{"aria-invalid": "true"}}) @inputotp.Slot(inputotp.SlotProps{Index: 3, Attributes: templ.Attributes{"aria-invalid": "true"}}) } @inputotp.Separator() @inputotp.Group() { @inputotp.Slot(inputotp.SlotProps{Index: 4, Attributes: templ.Attributes{"aria-invalid": "true"}}) @inputotp.Slot(inputotp.SlotProps{Index: 5, Attributes: templ.Attributes{"aria-invalid": "true"}}) } }}Four Digits
A common pattern for PIN codes. This uses the Pattern: inputotp.RegexpOnlyDigits prop.
package examples import "github.com/axadrn/shadcn-templ/v2/components/inputotp"package examples import "github.com/axadrn/shadcn-templ/v2/components/inputotp" templ InputOTPFourDigits() { @inputotp.InputOTP(inputotp.Props{Pattern: inputotp.RegexpOnlyDigits}) { @inputotp.Group() { @inputotp.Slot(inputotp.SlotProps{Index: 0}) @inputotp.Slot(inputotp.SlotProps{Index: 1}) @inputotp.Slot(inputotp.SlotProps{Index: 2}) @inputotp.Slot(inputotp.SlotProps{Index: 3}) } }}Alphanumeric
Use inputotp.RegexpOnlyDigitsAndChars to accept both letters and numbers.
package examples import "github.com/axadrn/shadcn-templ/v2/components/inputotp"package examples import "github.com/axadrn/shadcn-templ/v2/components/inputotp" templ InputOTPAlphanumeric() { @inputotp.InputOTP(inputotp.Props{Pattern: inputotp.RegexpOnlyDigitsAndChars}) { @inputotp.Group() { @inputotp.Slot(inputotp.SlotProps{Index: 0}) @inputotp.Slot(inputotp.SlotProps{Index: 1}) @inputotp.Slot(inputotp.SlotProps{Index: 2}) } @inputotp.Separator() @inputotp.Group() { @inputotp.Slot(inputotp.SlotProps{Index: 3}) @inputotp.Slot(inputotp.SlotProps{Index: 4}) @inputotp.Slot(inputotp.SlotProps{Index: 5}) } }}Form
package examples import (package examples import ( "github.com/axadrn/shadcn-templ/v2/components/button" "github.com/axadrn/shadcn-templ/v2/components/card" "github.com/axadrn/shadcn-templ/v2/components/field" "github.com/axadrn/shadcn-templ/v2/components/icon" "github.com/axadrn/shadcn-templ/v2/components/inputotp") templ InputOTPForm() { @card.Card(card.Props{Class: "mx-auto max-w-md"}) { @card.Header() { @card.Title() { Verify your login } @card.Description() { Enter the verification code we sent to your email address: <span class="font-medium">[email protected]</span>. } } @card.Content() { @field.Field() { <div class="flex items-center justify-between"> @field.Label(field.LabelProps{For: "otp-verification"}) { Verification code } @button.Button(button.Props{Variant: button.VariantOutline, Size: button.SizeXs}) { @icon.RefreshCw() Resend Code } </div> @inputotp.InputOTP(inputotp.Props{ID: "otp-verification", Required: true}) { @inputotp.Group(inputotp.GroupProps{Class: "*:data-[slot=input-otp-slot]:h-12 *:data-[slot=input-otp-slot]:w-11 *:data-[slot=input-otp-slot]:text-xl"}) { @inputotp.Slot(inputotp.SlotProps{Index: 0}) @inputotp.Slot(inputotp.SlotProps{Index: 1}) @inputotp.Slot(inputotp.SlotProps{Index: 2}) } @inputotp.Separator(inputotp.SeparatorProps{Class: "mx-2"}) @inputotp.Group(inputotp.GroupProps{Class: "*:data-[slot=input-otp-slot]:h-12 *:data-[slot=input-otp-slot]:w-11 *:data-[slot=input-otp-slot]:text-xl"}) { @inputotp.Slot(inputotp.SlotProps{Index: 3}) @inputotp.Slot(inputotp.SlotProps{Index: 4}) @inputotp.Slot(inputotp.SlotProps{Index: 5}) } } @field.Description() { <a href="#">I no longer have access to this email address.</a> } } } @card.Footer() { @field.Field() { @button.Button(button.Props{Type: button.TypeSubmit, Class: "w-full"}) { Verify } <div class="text-sm text-muted-foreground"> Having trouble signing in? <a href="#" class="underline underline-offset-4 transition-colors hover:text-primary">Contact support</a> </div> } } }}API Reference
InputOTP
The InputOTP component is the root that manages the slots and submits the combined value.
Group
The inputotp.Group component groups adjacent slots.
Slot
The inputotp.Slot component is a single character cell.
Separator
The inputotp.Separator component divides slot groups.