Vanish Input
An input that dissolves its text into pixel particles on submit, in sweep, scatter, or rise styles, with rotating placeholders.
npx shadcn@latest add https://webberui.com/r/vanish-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.
<VanishInput />
Installation
npx shadcn@latest add https://webberui.com/r/vanish-input.jsonOr, once registries are configured in components.json, install it as @webberui/vanish-input.
Usage
import { VanishInput } from "@/components/ui/vanish-input";
<VanishInput
placeholder="Type and press Enter⋯"
onSubmit={(value) => console.log(value)}
/>On submit (Enter or clicking the arrow on the right), the component paints the current text onto an offscreen canvas, samples it pixel by pixel into particles, then lets those particles drift away and fade out according to variant, clearing the input at the same time.
Controlled mode
const [value, setValue] = React.useState("");
<VanishInput
value={value}
onChange={setValue}
onSubmit={(v) => {
// clear the controlled value yourself after submitting
setValue("");
}}
/>Rotating placeholders
<VanishInput
placeholder={["Search the docs⋯", "or type a command", "Chinese and emoji supported 🎉"]}
variant="scatter"
/>Props
| Prop | Type | Default | Description |
|---|---|---|---|
placeholder | string | string[] | "輸入訊息⋯" | Placeholder text; passing an array rotates through them in order |
value | string | — | Controlled value (controlled mode) |
defaultValue | string | "" | Default value in uncontrolled mode |
onChange | (value: string) => void | — | Fires when the content changes |
onSubmit | (value: string) => void | — | Fires on submit with the submitted text |
variant | "sweep" | "scatter" | "rise" | "sweep" | Dissolve style: swept away / scattered / drifting upward |
duration | number | 0.9 | Duration of the dissolve animation (seconds) |
placeholderInterval | number | 3000 | Interval between rotating placeholders (ms) |
aria-label | string | first placeholder | Accessible name |
disabled | boolean | false | Disables input and submit |
className | string | — | Styles for the outer container |
How it works
- On submit, the text is sampled pixel by pixel through
getImageData, and the particle colour comes directly from the current text colour, so light and dark themes both line up automatically. - The canvas scales by
devicePixelRatio(capped at 2) to stay sharp, and aResizeObserverrecomputes its size to follow the input's width. - When the particle count exceeds the cap, particles are thinned out at even intervals to keep the glyph outlines while protecting performance; the
requestAnimationFrameloop stops automatically once every particle has faded out, and is also cleared when the component unmounts. - The input is carried by a native
<input>, so pressing Enter mid-composition only confirms the candidate and does not submit by accident.
Accessibility
- When the user has "reduce motion" enabled at the system level, submitting simply clears the input with no particle animation, and the placeholders stop rotating.
- The canvas is marked
aria-hidden, the input carries anaria-label(defaulting to the first placeholder), and the submit button carriesaria-label="送出". - The form is carried by a native
<form>with Enter submission and keyboard operation; thecaretstays visible.
Tag Input
A tag input — Enter or comma adds a pill chip (trimmed and deduplicated), Backspace on an empty input deletes the last one; chips spring in, and the rest close ranks with FLIP on removal, with no accidental adds mid-IME-composition.
File Dropzone
A drag-and-drop upload area — the magnetic dashed border leans toward the cursor and marches like a marquee, and the file list carries ring progress indicators.