Code input
Enter a one-time code by typing, pasting or, where the browser supports it, autofill from a message, and let the app know when the code is complete.
Check your phone
A code went to +1 •••• 0100
Enter a 6-character code.
246810 is the right one here.
<script setup lang="ts">import { ref } from 'vue'import { UiCard, UiField, UiCodeInput, UiButton } from 'grani' // The app checks the code; a wrong one is cleared, so the same code can be typed again.const otp = ref('') const error = ref('') const verified = ref(false) function check(value: string) { if (value === '246810') { verified.value = true error.value = '' } else { error.value = 'That code is not right. Try again.' otp.value = '' }} // A new code is on its way: the field and its error start over.function resend() { otp.value = '' error.value = '' verified.value = false}</script> <template> <UiCard title="Check your phone" description="A code went to +1 •••• 0100" class="w-full max-w-sm" > <UiField label="Code" :description="verified ? 'Checked: the code is right.' : '246810 is the right one here.'" :error="error || undefined" required > <UiCodeInput v-model="otp" @complete="check" @update:model-value="verified = false" /> </UiField> <template #footer> <span class="me-auto text-xs text-fg-muted">No code yet?</span> <UiButton variant="ghost" size="xs" @click="resend">Send it again</UiButton> </template> </UiCard></template>
Also called otp input, one-time code input, pin input, verification code.
Usage
<UiCodeInput v-model="value" /> With GraniResolver in the Vite config, a template needs no import: set it up once.
API
| Name · type | Default | Description |
|---|---|---|
| Value | ||
| modelValue*string | The entered code (v-model); empty is ''. | |
| Behavior | ||
| lengthnumber= 6 | 6 | Number of characters in the code, a whole number from 1 to 100; anything else is held to that and warned about in development. |
| inputModenumeric | text= "numeric" | "numeric" | Keyboard shown on a phone. numeric for SMS, PIN, and authenticator codes. text for codes with letters. |
| patternstring | RegExp | Allowed characters, tested one character at a time (not the whole code). A RegExp or its source. When omitted, numeric allows digits and text allows letters and digits. | |
| separatorboolean= false | false | Shows a short rule between the code groups. |
| Look | ||
| sizexs | sm | md= "md" | "md" | Step of the control ladder for the cells. |
| Form and access | ||
| disabledboolean= false | false | Prevents entering and editing the code. |
| invalidboolean= false | false | Marks the control as invalid. Inside UiField, the error state sets this automatically. |
| requiredboolean | Standalone only: a value is needed, said to a reader and to a form (native required). Inside UiField the field's required says so. | |
| namestring | Native name for forms. | |
| idstring | Own id when standalone. Inside UiField, the field's id takes precedence. | |
| ariaLabelstring | Accessible name when there is no visible label. | |
Attributes: class and style stay on the outer element; the rest (native attributes, aria-*, data-testid) reach the control inside it.
When to use another component
| When you need | Take |
|---|---|
| A password | Input type="password" |
| A number or a text of any length | Input |
| A code shown to copy and never typed | Copy button |