Stop wasting time building OTP inputs, start using Verino engine
One invisible input captures SMS autofill, password managers, and paste. One pure state machine drives React, Vue, Svelte, Alpine.js, Vanilla JS, Web Components, and React Native.
Type any 6 digits
Powered by @verino/vanilla, zero runtime dependencies
8
packages, one core
1,330+
tests passing
100%
core coverage
0
runtime dependencies
Frameworks
Same behavior, every framework
Pick a tab. This is the real adapter running, not a screenshot.
Type any 6 digits
Type any 6 digits
Type any 6 digits
Type any 6 digits
Type any 6 digits
Type any 6 digits
const otp = useOTP({ length: 6 })
<TextInput
{...otp.hiddenInputProps}
style={StyleSheet.absoluteFill}
/>
{otp.getSlots().map((slot) => (
<View style={styles.slot}>
<Text>{slot.value}</Text>
</View>
))} Native SMS autofill on iOS and Android. Can't run inside a browser, so this one's a code sample instead of a live demo. Try it in the Expo example.
Scenarios
One adapter, every scenario
Same @verino/vanilla instance, reconfigured
live. Including the built-in countdown and resend row.
Type the code to see it complete
Architecture
Built around one architectural decision
Six glued-together inputs break autofill. Verino uses one.
<input opacity:0 inset:0 z-index:1> ← real input
[div][div][div][div][div][div] ← decoration One real input, zero hacks
A single transparent input sits over purely decorative slot divs. The browser only ever sees one field, so SMS autofill, password managers, and IME all work natively.
Native SMS autofill
`autocomplete="one-time-code"` on the web, `textContentType="oneTimeCode"` and `autoComplete="sms-otp"` on React Native. Wired in automatically, every adapter.
Zero runtime dependencies
@verino/core has no DOM access and no dependencies at all. Every adapter imports only from the core, never from each other.
Fully accessible by default
Proper ARIA roles, labelled groups, and screen reader support come from using one real input, not from extra ARIA plumbing bolted on after.
Framework native, not ported
Each adapter is written for its framework: real Vue refs, a real Svelte store and action, a real React hook. Nothing is a wrapped port of another framework’s version.
Tested like it matters
1,330+ tests across 19 suites, Playwright E2E on chromium, firefox, webkit, and mobile chrome, and 100% statement, branch, function, and line coverage on the core.
Packages
Eight packages, one core
Pick the adapter for your stack. They all share the same behavior. Click a row to copy its install command.