One-time-password builder for short verification codes. Renders individual character cells instead of a single input.
length()fixes the exact code length and the renderer shows that many cellsdigitsOnly()restricts to numeric input and hints a numeric keyboardmask()hides the typed value behind a display character (e.g.•) while keeping the real value in form stategroups()splits the code into groups with a non-editable separator between them, like___-__- Combine with
validateOn: 'onChange'at hook level for instant validation as the user types
Defaults, inheritance & field methods
- defaultValue is
'' - type is
otp - Shared methods: see Base field builder
OTP-specific methods:
| Method | Type | Description |
|---|---|---|
length(length, message?) | length: number | Fixes the exact expected code length and syncs the descriptor min/max values. |
digitsOnly(message?) | message?: string | Rejects non-digit characters and hints a numeric keyboard. |
mask(char?) | char?: string (default '•') | Renders each filled cell with a masking character while the real value stays in form state. |
groups(sizes, separator?) | sizes: number[], separator?: string (default '-') | Splits the code into groups with a non-editable separator between them (e.g. [3, 2] renders ___-__). The total length becomes the sum of the sizes. |
Recipes
Patterns that showcase otp-specific strengths.
Standard 6-digit verification
Verification6.tsxtsx
| 1 | const schema = { |
| 2 | code: field.otp('Verification code') |
| 3 | .length(6) |
| 4 | .digitsOnly() |
| 5 | .required(), |
| 6 | } |
Short 4-digit PIN
Pin4.tsxtsx
| 1 | const schema = { |
| 2 | pin: field.otp('PIN') |
| 3 | .length(4) |
| 4 | .digitsOnly('Digits only') |
| 5 | .required(), |
| 6 | } |
Alphanumeric backup code
BackupCode.tsxtsx
| 1 | const schema = { |
| 2 | backupCode: field.otp('Backup code').length(8).required(), |
| 3 | } |
Masked verification code
MaskedOtp.tsxtsx
| 1 | const schema = { |
| 2 | code: field.otp('Verification code') |
| 3 | .length(6) |
| 4 | .digitsOnly() |
| 5 | .mask() |
| 6 | .required(), |
| 7 | } |
Grouped layout with a separator
GroupedOtp.tsxtsx
| 1 | const schema = { |
| 2 | code: field.otp('Verification code') |
| 3 | .groups([3, 2], '-') |
| 4 | .digitsOnly() |
| 5 | .required(), |
| 6 | } |
Auto-submit when the code is full
AutoSubmitOtp.tsxtsx
| 1 | const schema = { |
| 2 | code: field.otp('Verification code').length(6).digitsOnly().required(), |
| 3 | } |
| 4 | |
| 5 | const { Form, fields, state, submit } = useFormBridge(schema, { |
| 6 | validateOn: 'onChange', |
| 7 | }) |
| 8 | |
| 9 | useEffect(() => { |
| 10 | if (state.values.code?.length === 6 && state.isValid) { |
| 11 | submit() |
| 12 | } |
| 13 | }, [state.values.code, state.isValid, submit]) |
| 14 | |
| 15 | <Form onSubmit={verifyCode}> |
| 16 | <fields.code /> |
| 17 | </Form> |