Input OTP

One-time password input with auto-focus and paste support.

Stimulus Required

Installation

Add the component to your project:

rails generate shadcn:component input_otp

Usage

<%= render Shadcn::InputOtpComponent.new(length: 6, name: "otp") %>

Examples

With Separator

Group slots and add separators between them for common OTP formats like XXX-XXX.

<%= render Shadcn::InputOtpComponent.new(length: 6, name: "otp") do |otp| %>
  <% otp.with_group(slots: 3) %>
  <% otp.with_separator %>
  <% otp.with_group(slots: 3) %>
<% end %>

4-Digit PIN

<%= render Shadcn::InputOtpComponent.new(length: 4, name: "pin") %>

Numeric Only

Use pattern validation to restrict input to numbers only.

<%= render Shadcn::InputOtpComponent.new(length: 6, name: "otp", pattern: "^[0-9]*$") %>

Disabled

<%= render Shadcn::InputOtpComponent.new(length: 6, name: "otp", disabled: true) %>

In Form Field

Enter the 6-digit code sent to your email.

<%= render Shadcn::FieldComponent.new do |field| %>
  <% field.with_label { "Verification Code" } %>
  <% field.with_control do %>
    <%= render Shadcn::InputOtpComponent.new(length: 6, name: "verification_code") %>
  <% end %>
  <% field.with_description { "Enter the 6-digit code sent to your email." } %>
<% end %>

<%= render Shadcn::ButtonComponent.new(class_name: "w-full") { "Verify" } %>

API Reference

Props

API Reference

Prop Type Default Description
length Integer 6 Number of OTP digits
name String nil Input name for form submission
pattern String nil Regex pattern for validation
disabled Boolean false Whether the input is disabled
required Boolean false Whether the input is required
autocomplete String one-time-code Autocomplete attribute
class_name String nil Additional CSS classes to apply

Slots

Slot Props Description
with_group slots: 3 Creates a visual group of OTP slots
with_separator - Adds a separator (dash) between groups

Stimulus Controller

The Input OTP component requires the shadcn--input-otp Stimulus controller.

Stimulus Controller

This component requires JavaScript. The Stimulus controller docs provides interactivity.

Installation

Add to your config/importmap.rb:

pin "shadcn-rails", to: "shadcn/index.js"

Then in your app/javascript/controllers/index.js:

import { Application } from "@hotwired/stimulus"
import { registerShadcnControllers } from "shadcn-rails"

const application = Application.start()
registerShadcnControllers(application)

Or import just this controller:

import DocsController from "shadcn-rails/controllers/docs_controller"

application.register("docs", DocsController)

Targets

Name Description
slot Container for each OTP digit slot
input Individual input elements for each digit
hiddenInput Hidden input storing the full OTP value
caret Blinking caret indicator for empty focused slots

Values

Name Type Default Description
length Number 6 Number of OTP slots
pattern String Regex pattern for validation
disabled Boolean false Disabled state

Actions

Action Description
handleInput Processes input and auto-advances
handleKeydown Handles keyboard navigation
handlePaste Processes pasted OTP codes
clear Clears all inputs

TypeScript

Type definitions are included. Import types as needed:

import type { DocsController } from "shadcn-rails"

Features

  • Auto-advance: Cursor moves to next slot automatically after input
  • Paste support: Paste a full OTP code and it fills all slots
  • Keyboard navigation: Use arrow keys to move between slots
  • Backspace handling: Deletes and moves to previous slot
  • Pattern validation: Restrict input to specific characters
  • Hidden input: Full OTP value is stored in a hidden input for form submission

Accessibility

  • Each input is individually focusable via keyboard
  • Uses inputmode="numeric" for mobile numeric keyboard
  • First slot has autocomplete="one-time-code" for autofill
  • Visible focus ring indicates current active slot