Command Palette

Search for a command to run...

Loading component...

Components Free Smooth caret input

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/skiper106

How 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

  • SmoothInput and Input share 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 selectionchange listener keeps the caret in sync when clicking or using arrow keys; ResizeObserver recalculates 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

Click on the top right
to view the 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

Additionlly, if you find any bug or issue, feel free to
Drop a dm.

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.
Smooth caret input | Skiper UI | Skiper UI