OTP Input - National Design System

One-time password input component for verification codes with automatic focus management, paste support, and RTL-aware keyboard navigation

4-Digit OTP

Standard verification code input with automatic focus advance and hidden field sync

Verification Code

OTP with Separator

Use the separator element to visually group digits, commonly used for 6-digit verification codes split into two groups of three.

Verification Code

Validation States

OTP groups support validation states through the standard form data-status attribute. Status is automatically cleared when the user starts typing.

Enter code 1234

Built-in Features

What you get out of the box with zero configuration

Auto-initialization

Initializes on page load and detects dynamically added groups via MutationObserver. No manual setup needed.

Keyboard Navigation

Arrow keys move between inputs (RTL-aware). Backspace clears and moves back. Delete clears and moves forward. Auto-advances on digit entry.

Paste Support

Pasting a multi-digit string distributes digits across all inputs from the first position. Non-numeric characters are stripped automatically.

Hidden Field Sync

A hidden input with nds-otp-value class stays in sync with the concatenated value for form submission.

Custom Events

Fires nds:otpChange on any input change, nds:otpComplete when all digits are filled, and nds:otpClear on clear.

Accessibility

High-contrast mode thickens input borders. Reduced motion disables transitions. autocomplete="one-time-code" enables autofill on mobile.

Usage Guidelines

When and how to use OTP inputs effectively

When to Use

  • Verification codes sent via SMS, email, or authenticator apps
  • Two-factor authentication flows
  • Use 4 digits for simple codes, 6 digits with a separator for longer codes
  • Add autocomplete="one-time-code" on the first input for mobile autofill
  • Listen for nds:otpComplete to auto-submit when all digits are entered
  • For general text input, use form fields instead

JavaScript API

var group = document.querySelector('.nds-otp-group'); // Get the current value var code = NDS.OTP.getValue(group); // Set a value programmatically NDS.OTP.setValue(group, '1234'); // Clear all inputs and focus first NDS.OTP.clear(group); // Listen for completion (all digits entered) group.addEventListener('nds:otpComplete', function(e) { console.log('Code:', e.detail.value); }); // Listen for any change group.addEventListener('nds:otpChange', function(e) { console.log('Value:', e.detail.value, 'Filled:', e.detail.filled); }); // Validate OTP group manually var result = NDS.Forms.validateOtpGroup(group); // result: { valid: true/false, value: '1234', message: '...' } // Set/clear status NDS.Forms.setStatus({ element: group, status: 'error', message: 'Invalid code' }); NDS.Forms.clearStatus(group);
Was this page useful?
60% of users said Yes from 2843 Feedbacks