OTP Inputs

OTP inputs collect one-time passwords — the short verification codes used in two-factor authentication and passwordless login flows. Each digit gets its own character box, but a single <input> backs them all, so the value submits like any other form field and no JavaScript is required.

The input renders with inputmode="numeric" (mobile numeric keypad), autocomplete="one-time-code" (auto-fill from SMS), and a maxlength and pattern matching the digit count.

Basic OTP

By default, the component renders four character boxes. Click anywhere on the boxes to focus the input and start typing.

= daisy_otp(name: "verification_code")

6-Digit OTP

Pass length: to change the digit count. DaisyUI lays out up to 8 boxes.

= daisy_otp(name: "verification_code_6", length: 6)

Joined OTP

The otp-joined modifier connects the character boxes with no gap.

= daisy_otp(name: "pin", css: "otp-joined")

OTP Sizes

Size modifiers scale the boxes from otp-xs to otp-xl.

otp-xs
otp-sm
otp-md
otp-lg
otp-xl
- %w[otp-xs otp-sm otp-md otp-lg otp-xl].each do |size|
  .flex.items-center.gap-4
    = daisy_otp(name: "code_#{size.parameterize.underscore}", css: size)
    %span.font-mono.text-sm= size

OTP Colors

Color modifiers tint each box's border and caret; focusing a row adds a matching outline. All of the standard DaisyUI colors are available: otp-neutral, otp-primary, otp-secondary, otp-accent, otp-info, otp-success, otp-warning, and otp-error.

otp-primary otp-accent otp-error
%div{ class: "grid grid-cols-[auto_auto] items-center gap-4" }
  - %w[otp-primary otp-accent otp-error].each do |color|
    = daisy_otp(name: "code_#{color.parameterize.underscore}", css: color)
    %span.font-mono.text-sm= color

OTP in a Form

Combine the OTP with a fieldset and a submit button for a realistic verification flow. required: true participates in normal HTML form validation.

Since the value is a plain input, your JavaScript can read it like any other field — type a code and click Verify to see for yourself.

Enter Verification Code
:ruby
  verify_js = <<~JS.squish
    const value = document.querySelector("input[name='otp_code']").value;
    if (value.length < 6) {
      alert(`Hold on — that's only ${value.length} of 6 digits!`);
    } else {
      const sum = value.split("").reduce((total, digit) => total + Number(digit), 0);
      alert(`${value.split("").join(" + ")} = ${sum} — verified! 🎉`);
    }
  JS

= daisy_fieldset do |fieldset|
  - fieldset.with_legend { "Enter Verification Code" }

  = daisy_otp(name: "otp_code", length: 6, required: true)

  = daisy_button(title: "Verify", css: "btn-primary mt-4", html: { onclick: verify_js })
Made with by Profoundry .
Copyright © 2023-2026 Profoundry LLC.
All rights reserved.