I’m working on implementing an SMS OTP login in Svelte, similar to Auth0’s login flow. I’m using the PinInput component from BitsUI to handle the OTP input fields. My goal is to enable autofill for the OTP, particularly on iOS where autofill prompts appear after receiving an SMS with the code.
Here is my code:
<script lang="ts">
import { tick } from "svelte";
import { PinInput } from "bits-ui";
let pinInputRef: InstanceType<typeof PinInput.Root>;
let isPinVisible = $state(false);
async function handleSubmit(e: SubmitEvent) {
e.preventDefault();
if (!isPinVisible) {
isPinVisible = true;
await tick();
// Find and focus the first PIN input after it becomes visible
const firstInput = document.querySelector('[data-pin-input-input]') as HTMLInputElement;
firstInput?.focus();
}
}
</script>
<div class="flex flex-col justify-start items-center w-full p-6 gap-y-6">
<form onsubmit={handleSubmit} class="w-full max-w-72 md:max-w-96 flex flex-col relative">
<input
type="tel"
id="phone"
name="phone"
placeholder="(123) 456-7890"
class="bg-white autofill-white text-xl outline-none text-center mb-6"
autocomplete="tel-national"
enterkeyhint="next"
autofocus
/>
<!-- PIN Input with fade-in animation -->
<div class="transform transition-all duration-300 ease-out overflow-hidden {isPinVisible ? 'h-24 opacity-100 mb-6' : 'h-0 opacity-0'}">
<PinInput.Root
bind:this={pinInputRef}
enterkeyhint="go"
autocomplete="one-time-code"
class="grid grid-cols-6 gap-2 w-full max-w-lg mx-auto text-lg font-semibold text-neutral-500"
>
{#each Array(6) as _}
<PinInput.Input
class="aspect-square text-center bg-neutral-50 border-0 rounded-lg focus:ring-0 placeholder:text-neutral-300"
inputmode="numeric"
/>
{/each}
<PinInput.HiddenInput autocomplete="one-time-code" />
</PinInput.Root>
</div>
<!-- Button with slide-down animation -->
<button
type="submit"
class="bg-neutral-500 text-white px-4 py-2 rounded-full transition-all duration-300 ease-out transform"
>
{isPinVisible ? 'Verify' : 'Continue'}
</button>
</form>
</div>
I can successfully focus the PIN input element to bring up iOS’s keyboard, but the OTP autofill prompt doesn’t recognize the input fields. I suspect this might be because the PinInput.HiddenInput element is actually handling the autofill, and since it’s hidden, iOS treats this as a security precaution.
How can I modify my code so that iOS recognizes the PinInput fields for OTP autofill? If the hidden input is causing the issue, is there a workaround to make autofill work while using BitsUI’s PinInput component?