Command Palette
Search for a command to run...
Loading component...
Smooth caret input
A text input with a custom animated caret that smoothly tracks cursor position using canvas text measurement and spring physics, shown beside a standard input for comparison.
Dependencies
Interaction Type
| Type in the smooth input | |
| Watch the caret spring to each position | |
| Click to move the cursor | |
| Compare with the normal input below |
npx shadcn add @skiper-ui/skiper106How to use
Use SmoothInput when you want a playful, spring-animated caret instead of the native browser cursor. The native caret is hidden and replaced with a Framer Motion overlay that glides to each new position. Canvas text measurement keeps the caret aligned with the typed text, including password fields.
Pair it with the included Input wrapper when you need a matching control without the custom caret — the demo shows both side by side.
Basic usage
import { Input, SmoothInput } from "@/components/v1/skiper106"; const Demo = () => ( <div className="space-y-4 text-2xl"> <SmoothInput placeholder="smooth input" aria-label="Smooth caret input" /> <Input placeholder="normal input" className="caret-primary" aria-label="Normal input" /> </div> );
Controlled
import { useState } from "react"; import { SmoothInput } from "@/components/v1/skiper106"; const ControlledInput = () => { const [value, setValue] = useState("hello"); return ( <SmoothInput value={value} onChange={(e) => setValue(e.target.value)} placeholder="Type something…" aria-label="Controlled smooth input" /> ); };
Uncontrolled
<SmoothInput defaultValue="skiper" name="username" placeholder="username" autoComplete="username" />
Password field
SmoothInput measures password bullets per browser so the animated caret stays aligned with masked characters.
<SmoothInput type="password" placeholder="password" autoComplete="current-password" aria-label="Password" />
Standard input props
<SmoothInput id="message" name="message" disabled={isLoading} readOnly={isLocked} autoFocus onBlur={(e) => console.log(e.target.value)} onFocus={() => console.log("focused")} onKeyDown={(e) => e.key === "Enter" && e.currentTarget.blur()} className="text-primary" wrapperClassName="max-w-lg" />
Props | Description |
|---|---|
…input props | All native input attributes are supported and forwarded to the inner `<input>` (e.g. type, disabled, readOnly, name, id, autoComplete, onBlur, onFocus, onKeyDown, aria-*) |
value | Controlled value |
defaultValue | Initial value for uncontrolled usage |
onChange | Called when the input value changes |
className | Classes applied to the inner `<input>` |
wrapperClassName | Classes applied to the outer rounded container |
Input props
The plain Input export uses the same wrapper styling without the animated caret.
Props | Description |
|---|---|
…input props | All native input attributes are forwarded to the inner `<input>` |
className | Classes applied to the inner `<input>` |
wrapperClassName | Classes applied to the outer rounded container |
Demo
import { Skiper106 } from "@/components/v1/skiper106"; const DemoSkiper106 = () => ( <div className="h-screen w-full"> <Skiper106 /> </div> );
Notes
SmoothInputandInputshare identical wrapper and input styling — only the caret behavior differs- The native caret is hidden via
caretColor: transparent; a spring-animated overlay tracks cursor position instead - Caret position is measured with an offscreen canvas using the input's computed font, padding, and letter-spacing
- A document-level
selectionchangelistener keeps the caret in sync when clicking or using arrow keys;ResizeObserverrecalculates on layout changes - The custom caret hides while text is selected (non-collapsed selection)
- Password fields use browser-specific bullet characters (
•vs●) with adjusted font sizing where needed - Respects
prefers-reduced-motion— spring stiffness increases so the caret snaps instead of animating - This component is a playful UI experiment designed primarily for creative or unconventional interfaces. It is not intended for production or accessibility-critical scenarios. Use it for fun logins, novelty projects, or anywhere you want a whimsical input experience
DialKit setup
SmoothInput uses DialKit for live parameter tweaking. See the DialKit docs for installation and DialRoot setup.
Use the Smooth Input panel to tweak input type, placeholder, font size, and spring physics.
- Input type, placeholder, font size, spring (time or physics mode), clear value
Source code
Keep in mind
Most components here are recreations of the best out there. I don't claim to be the original creator. This is my attempt to reverse-engineer, replicate, and often add a few extra features. I've tried to credit everyone, but if I missed something, let me know.
Contact
License & Usage:
- Free to use and modify in both personal and commercial projects.
- Attribution to Skiper UI is required when using the free version.
- No attribution required with Skiper UI Pro.