📄 radixui/primitives/docs/components/one-time-password-field

File: one-time-password-field.md | Updated: 11/15/2025

Source: https://www.radix-ui.com/primitives/docs/components/one-time-password-field

Radix Homepage

Made by WorkOS

Radix Homepage

Made by WorkOS

ThemesThemes PrimitivesPrimitives IconsIcons ColorsColors

Documentation Case studies Blog

Search

/

Overview

Introduction Getting started Accessibility Releases

Guides

Styling Animation Composition Server-side rendering

Components

Accordion Alert Dialog Aspect Ratio Avatar Checkbox Collapsible Context Menu Dialog Dropdown Menu Form

Preview
Hover Card Label Menubar Navigation Menu One-Time Password Field

Preview
Password Toggle Field

Preview
Popover Progress Radio Group Scroll Area Select Separator Slider Switch Tabs Toast Toggle Toggle Group Toolbar Tooltip

Utilities

Accessible Icon Direction Provider Portal Slot Visually Hidden

Components

One-Time Password Field

A group of single-character text inputs to handle one-time password verification.

index.jsxindex.jsxstyles.cssstyles.css

CSS

import * as React from "react";
import { unstable_OneTimePasswordField as OneTimePasswordField } from "radix-ui";

const OneTimePasswordFieldDemo = () => (
	<OneTimePasswordField.Root className="OTPRoot">
		<OneTimePasswordField.Input className="OTPInput" />
		<OneTimePasswordField.Input className="OTPInput" />
		<OneTimePasswordField.Input className="OTPInput" />
		<OneTimePasswordField.Input className="OTPInput" />
		<OneTimePasswordField.Input className="OTPInput" />
		<OneTimePasswordField.Input className="OTPInput" />
		<OneTimePasswordField.HiddenInput />
	</OneTimePasswordField.Root>
);

export default OneTimePasswordFieldDemo;

Features

Keyboard navigation mimicking the behavior of a single input field

Overriding values on paste

Password manager autofill support

Input validation for numeric and alphanumeric values

Auto-submit on completion

Hidden input to provide a single value to form data

Anatomy


Import all parts and piece them together.

import { unstable_OneTimePasswordField as OneTimePasswordField } from "radix-ui";

export default () => (
	<OneTimePasswordField.Root>
		{/* one Input for each character of the value */}
		<OneTimePasswordField.Input />
		{/* single HiddenInput to store the full value */}
		<OneTimePasswordField.HiddenInput />
	</OneTimePasswordField.Root>
);

API Reference


Root

Contains all the parts of a one-time password field.

| Prop | Type | Default | | --- | --- | --- | | asChild<br><br>Prop description | boolean | false | | autoComplete<br><br>Prop description | enum<br><br>See full type | one-time-code | | autoFocus<br><br>Prop description | boolean | No default value | | value<br><br>Prop description | string | No default value | | defaultValue<br><br>Prop description | string | No default value | | onValueChange<br><br>Prop description | function<br><br>See full type | No default value | | autoSubmit<br><br>Prop description | boolean | false | | onAutoSubmit<br><br>Prop description | function<br><br>See full type | No default value | | disabled<br><br>Prop description | boolean | false | | dir<br><br>Prop description | enum<br><br>See full type | "ltr" | | orientation<br><br>Prop description | enum<br><br>See full type | "vertical" | | form<br><br>Prop description | string | No default value | | name<br><br>Prop description | string | No default value | | placeholder<br><br>Prop description | string | No default value | | readOnly<br><br>Prop description | boolean | false | | sanitizeValue<br><br>Prop description | function<br><br>See full type | No default value | | type<br><br>Prop description | enum<br><br>See full type | "text" | | validationType<br><br>Prop description | enum<br><br>See full type | "numeric" |

| Data attribute | Values | | --- | --- | | [data-orientation] | "vertical" \| "horizontal" |

Input

Renders a text input representing a single character in the value.

| Prop | Type | Default | | --- | --- | --- | | asChild<br><br>Prop description | boolean | false |

| Data attribute | Values | | --- | --- | | [data-index] | The index corresponding with the index of the character relative to the root field value |

HiddenInput

| Prop | Type | Default | | --- | --- | --- | | asChild<br><br>Prop description | boolean | false |

Examples


Basic usage

// This will render a field with 6 inputs, for use with
// 6-character passwords. Render an Input component for
// each character of accepted password's length.
<OneTimePasswordField.Root>
	<OneTimePasswordField.Input />
	<OneTimePasswordField.Input />
	<OneTimePasswordField.Input />
	<OneTimePasswordField.Input />
	<OneTimePasswordField.Input />
	<OneTimePasswordField.Input />
	<OneTimePasswordField.HiddenInput />
</OneTimePasswordField.Root>

Segmented controls

The Root component accepts arbitrary children, so rendering a visually segmented list is as simple as rendering separators between inputs. We recommend hiding decorative elements from assistive tech with aria-hidden and avoid rendering other meaningful content within Root since each child element is expected to belong to the parent with the group role.

<OneTimePasswordField.Root>
	<OneTimePasswordField.Input />
	<Separator.Root aria-hidden />
	<OneTimePasswordField.Input />
	<Separator.Root aria-hidden />
	<OneTimePasswordField.Input />
	<Separator.Root aria-hidden />
	<OneTimePasswordField.Input />
	<OneTimePasswordField.HiddenInput />
</OneTimePasswordField.Root>

Auto-submit form when password is entered

Use the autoSubmit prop to submit an associated form when all inputs are filled.

function Verify({ validCode }) {
	const PASSWORD_LENGTH = 6;
	function handleSubmit(event) {
		event.preventDefault();
		const formData = event.formData;
		if (formData.get("otp") === validCode) {
			redirect("/authenticated");
		} else {
			window.alert("Invalid code");
		}
	}
	return (
		<form onSubmit={handleSubmit}>
			<OneTimePasswordField.Root name="otp" autoSubmit>
				{PASSWORD_LENGTH.map((_, i) => (
					<OneTimePasswordField.Input key={i} />
				))}
				{/* HiddenInput is required for the form to have data associated with the field */}
				<OneTimePasswordField.HiddenInput />
			</OneTimePasswordField.Root>
			<button>Submit</button>
		</form>
	);
}

Controlled value

function Verify({ validCode }) {
	const [value, setValue] = React.useState("");
	const PASSWORD_LENGTH = 6;
	function handleSubmit() {
		if (value === validCode) {
			redirect("/authenticated");
		} else {
			window.alert("Invalid code");
		}
	}
	return (
		<OneTimePasswordField.Root
			autoSubmit
			value={value}
			onAutoSubmit={handleSubmit}
			onValueChange={setValue}
		>
			{PASSWORD_LENGTH.map((_, i) => (
				<OneTimePasswordField.Input key={i} />
			))}
		</OneTimePasswordField.Root>
	);
}

Accessibility


At the time of writing, there is no singular established pattern in WCAG guidelines for implementing one-time password fields as separate inputs. The behavior aims to get as close as possible to having the field act as a single input, with a few exceptions to match user expectations based on our initial research, testing, and gathering feedback.

This component is implemented as input elements within a container with a role of group to indicate that child inputs are related. Inputs can be navigated and focused using direction keys, and typing input will move focus to the next input until the last input is reached.

Pasting a value into the field will replace the contents of all inputs, regardless of the currently focused input. Based on our research this seems to align with most user expectations, where values are often pasted from password-managers or an email.

Keyboard Interactions

| Key | Description | | --- | --- | | Enter | Attempts to submit an associated form if one is found | | Tab | Moves focus to the next focusable element outside of the Root | | Shift + Tab | Moves focus to the previous focusable element outside of the Root | | ArrowDown | Moves focus to the next Input when orientation is vertical. | | ArrowUp | Moves focus to the previous Input when orientation is vertical. | | ArrowRight | Moves focus to the next Input when orientation is horizontal. | | ArrowLeft | Moves focus to the previous Input when orientation is horizontal. | | Home | Moves focus to the first Input. | | End | Moves focus to the last Input. | | Delete | Removes the character in the currently focused Input and shifts later values back | | Backspace | Removes the character in the currently focused Input and moves focus to the previous Input | | Command + Backspace | Clears the value of all Input elements |

PreviousNavigation Menu

NextPassword Toggle Field

Edit this page on GitHub.