PIN Input
A sequence of single-character input cells for entering PINs, OTPs, and verification codes.
Enter verification code
We sent a 6-digit code to your email.
Installation
biji-ui = { version = "0.5.0", features = ["pin_input"] }
Usage
use leptos::prelude::*;
use biji_ui::components::pin_input;
#[component]
pub fn MyPinInput() -> impl IntoView {
view! {
<pin_input::Root
length=6
class="flex gap-2"
on_complete={Callback::new(|code: String| {
leptos::logging::log!("PIN complete: {code}");
})}
>
<pin_input::Cell index=0 class="w-10 h-12 text-center text-lg rounded-md border border-input bg-background focus:outline-none focus:ring-2 focus:ring-ring data-[filled]:border-primary" />
<pin_input::Cell index=1 class="w-10 h-12 text-center text-lg rounded-md border border-input bg-background focus:outline-none focus:ring-2 focus:ring-ring data-[filled]:border-primary" />
<pin_input::Cell index=2 class="w-10 h-12 text-center text-lg rounded-md border border-input bg-background focus:outline-none focus:ring-2 focus:ring-ring data-[filled]:border-primary" />
<pin_input::Cell index=3 class="w-10 h-12 text-center text-lg rounded-md border border-input bg-background focus:outline-none focus:ring-2 focus:ring-ring data-[filled]:border-primary" />
<pin_input::Cell index=4 class="w-10 h-12 text-center text-lg rounded-md border border-input bg-background focus:outline-none focus:ring-2 focus:ring-ring data-[filled]:border-primary" />
<pin_input::Cell index=5 class="w-10 h-12 text-center text-lg rounded-md border border-input bg-background focus:outline-none focus:ring-2 focus:ring-ring data-[filled]:border-primary" />
</pin_input::Root>
}
}
RootWith
Use <RootWith> when you need direct access to PinInputState inside the children. The let:s binding exposes s.value and s.is_complete as reactive signals — no callbacks needed for reading state.
Enter 4-digit code
use leptos::prelude::*;
use biji_ui::components::pin_input;
#[component]
pub fn MyPinInput() -> impl IntoView {
const CELL_CLS: &str = "w-11 h-13 text-center text-xl font-mono rounded-md border \
border-input bg-background focus:outline-none focus:ring-2 focus:ring-ring \
data-[filled]:border-primary placeholder:text-muted-foreground/40";
view! {
<pin_input::RootWith length=4 class="flex flex-col items-center gap-4" let:s>
<div class="flex gap-2">
<pin_input::Cell index=0 class={CELL_CLS} />
<pin_input::Cell index=1 class={CELL_CLS} />
<pin_input::Cell index=2 class={CELL_CLS} />
<pin_input::Cell index=3 class={CELL_CLS} />
</div>
<p class="text-xs text-muted-foreground">
{move || if s.is_complete.get() {
format!("Code: {}", s.value.get())
} else {
format!("{}/4 digits entered", s.value.get().len())
}}
</p>
</pin_input::RootWith>
}
}
API Reference
Root / RootWith
| Name | Type | Default | Description |
|---|---|---|---|
| class | String | "" | CSS class applied to the root wrapper element. |
| length | usize | 4 | The total number of PIN cells. |
| disabled | bool | false | When true, all cells are disabled. |
| placeholder | String | "○" | Placeholder character shown in each empty cell. |
| on_complete | Option<Callback<String>> | None | Callback fired with the full PIN string when every cell is filled. |
Cell
| Name | Type | Default | Description |
|---|---|---|---|
| index | usize | Zero-based position of this cell within the PIN input. | |
| class | String | "" | CSS class applied to the input element. |
Data Attributes
| Attribute | Description |
|---|---|
| data-filled | Present on Cell when the cell has a value entered. |
| data-disabled | Present on Root and Cell when the input is disabled. |
| data-index | The zero-based index of the cell. Present on Cell. |
Keyboard Navigation
| Key | Description |
|---|---|
| 0–9 / Any character | Types a character into the current cell and advances focus to the next cell. |
| Backspace | Clears the current cell. If already empty, clears the previous cell and moves focus back. |
| ArrowLeft | Moves focus to the previous cell. |
| ArrowRight | Moves focus to the next cell. |
| Paste (Ctrl+V / Cmd+V) | Pastes a string of characters distributed across cells starting at the focused cell. |