Forms

Input OTP

A segmented one-time-password input.

Rust
1use herogpui::components::input_otp::{InputOTP, OtpState};

Usage

Custom slot content inherits 14px/20px text; the built-in digits use 18px/24px text.

Rust
1use herogpui::prelude::{InputOTP, Label, Link, OtpState};
2use gpui::prelude::*;
3use gpui::px;
4
5gpui::div()
6    .flex()
7    .flex_col()
8    .items_start()
9    .gap(px(12.))
10    .children(vec![
11        Label::new("Verify account").into_any_element(),
12        muted_para("We've sent a code to a****@gmail.com", cx),
13        InputOTP::new(self.otp.clone())
14            .separator()
15            .on_complete(cx.listener(|this, code: &str, _, cx| {
16                this.otp_done = code.to_owned();
17                cx.notify();
18            }))
19            .into_any_element(),
20        gpui::div()
21            .flex()
22            .items_center()
23            .gap(px(5.))
24            .child(muted_para(
25                &if done.is_empty() {
26                    "Didn't receive a code?".to_owned()
27                } else {
28                    format!("Complete: {done}")
29                },
30                cx,
31            ))
32            .child(Link::new("input-otp-resend").label("Resend").href("#"))
33            .into_any_element(),
34    ])

Live HeroGPUI compiled to WebAssembly. Select any example without loading another WASM instance.

Anatomy

Rust types

InputOTP

Input OTP is assembled from these builders. The API reference lists each one.

Customization

Appearance builders and theme tokens Input OTP uses.

Styling

Input OTP styling
StyleDescription
text_size + line_heightCustom slot content inherits 14px/20px text; the built-in digits use 18px/24px text.
flex + items_center + gap(px(8.))Root container geometry.
flat row; no distinct group wrapperSlot group geometry.
h(px(40.)) + w(px(38.)) + alignment matchSlot dimensions and alignment.
field/default background + field shadow + field_radius + 14px semiboldDefault slot chrome and typography.
instant hover/focus/invalid swapsAnimated slot-state interpolation.
hover(default.hover background)Hovered slot chrome.
with_focus_ring on the cursor slotActive slot chrome.
value drawn without a distinct filled backgroundFilled slot background.
whole-row disabled opacityDisabled slot treatment.
danger border on non-active slotsInvalid slot treatment.
18px/24px text; no tracking or entrance animationFilled character typography and entrance motion.
2x16px hairline + anim::caret_blinkFake caret geometry and motion.
6x2px shrink-0 hairline radius + separator colorSeparator geometry and color.
FieldVariant::Secondary default background without field shadowSecondary slot variant.

API reference

Builders

Input OTP builders
BuilderTypeDefaultDescription
with_length(cx, length)cx, length— (required)Number of input slots.
value(code, cx)code, cxControlled value.
on_change(callback)Fn(value: string) -> ()Handler called whenever the value changes.
on_complete(callback)Fn(value: string) -> ()Handler called when all slots are filled.
variant(FieldVariant)FieldVariantFieldVariant::PrimaryVisual treatment of the slots.
slot(render)AnyElementComposition of Group, Slot and Separator parts.
is_disabled(bool)boolfalseWhether the input is disabled.
is_invalid(bool)boolfalseWhether the input is invalid.
validation_errors(errors)errorsServer-side or custom validation errors.
pattern(OtpPattern)OtpPatternRegular expression for accepted characters.
text_align(OtpTextAlign)OtpTextAlignOtpTextAlign::LeftText alignment within each slot.
placeholder(impl Into<SharedString>)impl Into<SharedString>Placeholder text for empty slots.
paste_transformer(callback)Fn(text: string) -> stringTransforms pasted text before filling the slots.
name(value)stringName used by form_field(); explicit Form registration is required.
auto_focus(bool)boolFocuses the first slot on mount.

Parts

Input OTP parts
PartDescription
InputOTPRoot field and editing surface.
InputOTPGroup of slots; implicit in the flat GPUI row.
InputOTPOne indexed character slot.
InputOTPOptional separator bar after each three slots.

States

Input OTP states
BuilderStateDescription
cell.hover(background)HoveredHovered slot surface.
focused && i == cursor + with_focus_ringActiveFocused slot carrying the fake caret.
cell_ch != ' ' value branchFilledSlot containing a character.
is_disabled focus/key gates + row opacityDisabledDisabled root and slots.
validation::resolve + danger border + ErrorMessageInvalidInvalid field treatment.