Smooth Caret Input
An input with a spring-physics smooth caret — the custom caret glides to the text position on a spring, in bar, block, or underline shapes.
npx shadcn@latest add https://webberui.com/r/smooth-caret-input.jsonPlayground
Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.
<SmoothCaretInput />
Installation
npx shadcn@latest add https://webberui.com/r/smooth-caret-input.jsonThe install also writes the --wb-duration-fast and --wb-ease-out CSS variables into your global stylesheet.
Usage
import { SmoothCaretInput } from "@/components/ui/smooth-caret-input";
<SmoothCaretInput
placeholder="Start typing⋯"
onChange={(value) => console.log(value)}
/>Controlled mode:
const [value, setValue] = React.useState("");
<SmoothCaretInput value={value} onChange={setValue} caretShape="block" />Props
| Prop | Type | Default | Description |
|---|---|---|---|
value | string | — | Controlled value; when omitted, the component manages it internally (uncontrolled mode) |
defaultValue | string | "" | Initial value in uncontrolled mode |
onChange | (value: string) => void | — | Fires when the value changes |
placeholder | string | — | Placeholder text |
disabled | boolean | false | Disables input |
caretShape | "bar" | "block" | "underline" | "bar" | Caret shape |
caretColor | string | text colour | Caret colour (any CSS colour) |
stiffness | number | 650 | Spring stiffness; higher settles faster |
damping | number | 42 | Spring damping; higher springs back less |
name | string | — | The input's name, for form submission |
id | string | — | The input's id, for binding a label |
aria-label | string | placeholder | Accessible label |
How it works
- Spring following: a hidden mirror span measures the width of the text before the caret to convert it into a pixel position, which
useSpringthen glides smoothly toward - Caret movement detection: it listens to
selectionchange, so arrow keys, clicks, and selections all recompute the position immediately and pause the blink, which resumes once the glide finishes - Overflow handling: when the text exceeds the width and scrolls horizontally,
scrollLeftis subtracted so the caret stays inside the visible area - IME safe: during Bopomofo (Zhuyin) or Pinyin composition, the mirror includes the composing text, so the caret keeps following the correct position
Accessibility
- The custom caret carries
aria-hiddenso it does not interfere with screen reading; theinputtakes thearia-label/name/id - When the user has "reduce motion" enabled at the system level, the native caret is restored after mount and the spring and blink animations are disabled
- When no
aria-labelis provided, theplaceholderis used automatically, guaranteeing a readable name
AI Branch Switcher (React)
A React version switcher for a single AI reply: a ‹ 2 / 3 › pager slides the content directionally, regenerate opens a new branch with a typing skeleton, an optional mini branch tree sits above the bubble, ←/→ switch versions while the bubble has focus, plus copy and model/time captions.
OTP Input
A segmented verification-code input — a single invisible input sits over the visual cells, with paste support, an error shake, and a success ripple that turns the cells green.