The OtpInput component collects a one time code, the kind sent by SMS or made by an authenticator app. Each character has its own box, typing moves forward by itself, and pasting a whole code fills every box at once.
Example
Import the component and bind a string. The value is the code as one piece of text, so you can send it straight to your server.
value: ""
<script>
import { OtpInput } from "theui-svelte";
let code = $state("")
</script>
<OtpInput bind:value={code}>Verification code</OtpInput>Length
The length prop sets how many boxes there are. The default is 6.
<OtpInput length={4} />
<OtpInput length={8} />Types
The type prop decides what may be typed: number (default) takes digits only and opens the number keypad on a phone, text takes letters and digits, and password hides what has been typed.
<OtpInput type="number" length={4} />
<OtpInput type="text" length={4} />
<OtpInput type="password" length={4} />Separator
Use separatorAfter to break a long code into two halves, the way printed codes are grouped.
<OtpInput length={6} separatorAfter={3} />On Complete
The oncomplete callback runs as soon as the last box is filled, which is the moment to check the code without making the user press a button.
Waiting for four digits...
<OtpInput length={4} oncomplete={(value) => verify(value)}>
Enter the code
</OtpInput>Sizes and Variants
Sizes run from sm to xl, and the variant can be bordered or flat. Both are inherited from a Form or a Fieldset.
<OtpInput length={4} size="sm" />
<OtpInput length={4} size="lg" />
<OtpInput length={4} variant="flat" />
<OtpInput length={4} rounded="full" />In a Form
Give the component a name and the whole code is submitted as one field, not as six. Add autofocus when the code is the only thing on the page.
<form method="POST">
<OtpInput name="otp" length={6} helperText="We sent a code to your phone">
One time code
</OtpInput>
<button>Verify</button>
</form>Animation Speed
The animationSpeed prop sets how quickly each box reacts to focus and hover. It takes "none", "slower", "slow", "normal", "fast" and "faster", and the default is "normal". Inside a Form or a Fieldset the value is inherited, so one setting covers every field at once.
<OtpInput animationSpeed="slower">Slower</OtpInput>
<OtpInput animationSpeed="faster">Faster</OtpInput>
<OtpInput animationSpeed="none">No animation</OtpInput>Reset Styles
Set reset to true to drop the border, background and focus ring of every box and keep only the classes you pass. The boxes stay in a row and keep their width, so paste and the arrow keys are unaffected. The default is false, and a Form or a Fieldset can set it for every field it holds.
<OtpInput reset boxClasses="border-0 border-b-2 border-gray-400">
Underline only
</OtpInput>Customization
Use boxClasses for the boxes themselves, and placeholder to show a character in the empty ones.
<OtpInput length={4} placeholder="•" />
<OtpInput length={4} boxClasses="w-12 text-xl font-semibold" />
<OtpInput length={4} variant="flat" boxClasses="border-b-4 text-2xl" />Keyboard and Paste
Typing a character moves to the next box. Backspace clears the box, or steps back and clears the one before when the box is already empty. The left and right arrows move between boxes, Home and End jump to the first and last, and Delete clears without moving. Pasting a code anywhere in the row fills the boxes from that point on, so a copied code never has to be typed.
Accessibility
Every box is a real input with its own name, such as "Character 3 of 6", which you can reword with the boxLabel prop. The row is a group, so a screen reader user hears the label of the field before the boxes. The first box carries autocomplete="one-time-code", which lets phones offer the code from an SMS, and the number type opens the numeric keypad. When you pass a name, the code is submitted once as a whole rather than as separate characters.