Skip to content

shadcn.io is an independent project built on shadcn/ui

Free

Email Verification Flow Input OTP for Svelte

An email check screen with a mail icon, a code sent to user@example.com, Verify Email, and Resend code.

UI library: Ark UI

A mail icon sits above Check your email and user@example.com. Six boxes follow, then Verify Email and a Resend code button.

Use this after you email a code. If the message can carry a link, a Button on that page can confirm without typing. In Svelte, Ark UI does not verify the code.

Preview

Preview width

Installation

MCP · Pro
Make your AI a shadcn expert
Your agent installs components, builds pages and restyles your app from one prompt, in Claude Code, Cursor or VS Code.
Part of Pro

Anatomy

Input OTP anatomy: root, group, separator, slot

Usage

<div class="flex w-full max-w-sm flex-col gap-6"><div class="flex flex-col items-center gap-2 text-center"><div class="flex size-12 items-center justify-center rounded-full bg-primary/10"><MailIcon aria-hidden="true" class="size-6 text-primary" /></div><h2 class="font-semibold text-2xl">Check your email</h2><p class="text-muted-foreground text-sm">We sent a verification code to{" "}<span class="font-medium">user@example.com</span></p></div><form class="flex flex-col gap-6" onsubmit={handleSubmit}><Field invalid={error !== ""}><FieldLabel class="sr-only">Verification code</FieldLabel><div class="flex justify-center"><InputOTP onValueChange={handleValueChange} value={value}><InputOTPGroup><InputOTPSlot class="bg-background" index={0} /><InputOTPSlot class="bg-background" index={1} /><InputOTPSlot class="bg-background" index={2} /></InputOTPGroup><InputOTPSeparator /><InputOTPGroup><InputOTPSlot class="bg-background" index={3} /><InputOTPSlot class="bg-background" index={4} /><InputOTPSlot class="bg-background" index={5} /></InputOTPGroup></InputOTP></div><FieldError class="text-center">{error}</FieldError></Field><Button class="w-full" disabled={isVerifying} type="submit">{#if isVerifying}{"Verifying..."}{:else}{"Verify Email"}{/if}</Button></form><div class="text-center text-muted-foreground text-sm">Didn’t receive the email?{" "}<Button class="h-auto p-0 font-normal" onclick={handleResend} type="button" variant="link">Resend code</Button></div>{#if status}<p class="text-center text-muted-foreground text-sm" role="status">{status}</p>{:else}{/if}</div>

FAQ

How is the header built?

A MailIcon in a size-12 circle with bg-primary/10, a heading, and a line naming the address in font-medium. The icon has aria-hidden.

What happens when Verify Email is pressed early?

A code shorter than six shows Verification code must be 6 characters. in FieldError, centered under the field.

How does the button show progress?

While verifying, the button is disabled and reads Verifying...; a timer stands in for the request. The example shows the result in a status line under the form instead of calling an API. Replace that with your request, and show its error in FieldError when the code is rejected.

What does Resend code do?

It clears the field and any error and says a new code is on its way. Call your API there, and add a countdown as the SMS Verification example does.

How is the code field named?

A FieldLabel with sr-only reads Verification code, so the field is named for screen readers without a visible label under the heading. Field links FieldLabel to the code field inside it, so there is no id to match.

How is the value kept?

In state, written on every change. The value is an array of strings, one per slot, and each change arrives as details.value.

API Reference

Field

Sets invalid={error !== ""}. The rest is on the Field page.

Input OTP

Sets onValueChange={handleValueChange}, value={value}, index={0}, index={1}, index={2} and index={3}. The rest is on the Input OTP page.

Button

Sets disabled={isVerifying}, type="submit", onclick={handleResend}, type="button" and variant="link". The rest is on the Button page.