Building Robust One-Time Passcode (OTP) Inputs with HTML

Web Development

Discover how to create highly functional and accessible one-time passcode (OTP) input fields using minimal HTML, emphasizing clarity and progressive enhancement.

By Tyler Sticka Published on November 11th, 2025

If you've logged into an online service in the past decade, you've likely encountered a request to fill a one-time passcode (OTP) field with digits from a text message, email, or authenticator app:

Slack's OTP entry form

Despite the widespread use of this pattern, it often causes unnecessary complexity for web developers. This is especially true when developers fixate on the trend of segmenting the input to visually convey the passcode's length—a modern take on the classic input mask.

Why else would so many developers fall into the trap of building custom <input> replacements, chaining multiple <input> elements together, or burdening their projects with yet another third-party dependency?

If you find yourself in this situation, I have great news! You can implement a fully functional OTP input today without any elaborate CSS hacks or JavaScript frameworks.

All you need is some HTML.

Basic Markup

The magic of a one-time passcode input lies within a single <input> element:

<input
  type="text"
  inputmode="numeric"
  autocomplete="one-time-code"
  maxlength="6"
>

Let's break down each of its attributes:

  • type="text": Although our passcode consists of numbers, it is not a numerical value in the mathematical sense (e.g., 000004 is distinct from 4). Following the HTML specification, we set type="text".
  • inputmode="numeric": This attribute enables a numeric virtual keyboard on touch devices, improving user experience.
  • autocomplete="one-time-code": This adds crucial support for autofill features, whether from password managers or via SMS.
  • maxlength="6": This prevents users from entering more characters than required.

We can further enhance client-side validation by adding two more attributes:

<input
  type="text"
  inputmode="numeric"
  autocomplete="one-time-code"
  maxlength="6"
  pattern="\d{6}"
  required
>
  • pattern="\d{6}": This defines the expected code format, in this case, exactly six ({6}) numeric digits (\d).
  • required: This informs the browser that the field must have a value that satisfies the defined pattern.

Example: In a Form

Now that all OTP-specific features are addressed, let's provide context by embedding our input within a complete form. This includes a heading, a clear label, a submit button, and a support link for alternative options:

<form action="..." method="post">
  <h2>Verify Account</h2>
  <label for="otp">Enter the 6-digit numeric code sent to the number ending in 55</label>
  <input
    type="text"
    id="otp"
    inputmode="numeric"
    autocomplete="one-time-code"
    maxlength="6"
    pattern="\d{6}"
    required
  >
  <button>Continue</button>
  <a href="...">Try another way...</a>
</form>

Notice how the label explicitly states the intended length and format of the passcode. No input mask, icon, or visual affordance can match the accessibility and clarity of straightforward text!

With these additions, our OTP pattern is functionally complete!

Demo: With Styles

Since we've established all critical functionality within our HTML, we are free to style our form according to project requirements.

In this example, I've opted for a large, monospaced font with some letter-spacing to ensure each digit is distinct and readable. I'm also utilizing the :invalid pseudo-class to visually reduce the prominence of the <button> element until a valid code is entered.

CodePen Embed Fallback

Demo: Enhanced

Having a strong foundation solely in HTML and CSS doesn't prevent us from leveraging JavaScript for progressive enhancements.

Here's the same demo, but with a simple input mask web component to indicate remaining characters:

CodePen Embed Fallback

Because this enhancement builds upon existing HTML patterns rather than replacing them entirely, the code remains remarkably tiny—less than a kilobyte without any optimization or compression.

Takeaways

  • All critical features of a one-time passcode input can be achieved using HTML alone.
  • Clear labels and instructive text are more vital than any visual affordance.
  • Custom designs and behaviors should be layered on as progressive enhancements.
  • This approach offers quicker implementation and avoids many common performance and accessibility pitfalls.

Tyler Sticka has over 20 years of experience designing interfaces for the web and beyond. He co-owns Cloud Four, where he provides multidisciplinary creative direction for various organizations. He is also an artist, writer, speaker, and developer. You can follow Tyler on his personal site, Mastodon, or Bluesky.