A per-character vertical text roll animation for label swaps and micro-interactions, powered by Motion. React.
Made by AxylInstallation
File Structure
Usage
TextRoll does not animate on hover or click by itself — update the text prop (same as SlotText from slot-text):
Click the preview button to see Copy → Copied roll. Hover alone does not trigger the effect.
Direction, stagger, and bounce:
Chromatic sweep on roll — pass a color function or use the built-in helper:
For aligned short labels (Copy → Copied), keep skipUnchanged at the default true so shared letters stay put. Turn it off when lengths differ so the whole line rolls uniformly.
Props
| Prop | Type | Default |
|---|---|---|
text? | string | - |
options? | TextRollOptions | - |
direction? | "up" | "down" | "down" |
stagger? | number | 45 |
duration? | number | 300 |
bounce? | number | 0.6 |
skipUnchanged? | boolean | true |
chromatic? | boolean | false |
className? | string | - |
When to Use
Use TextRoll for micro-interactions where a label changes state: copy buttons, submit confirmations, tab switches, quantity controls, or any short string that toggles between two values. The per-character roll gives discrete state changes a physical weight that a plain text swap lacks.
Keep labels short (1–10 characters) for the effect to be legible. For longer text swaps or multi-word labels, consider TextMorph or a simple fade.
Accessibility
TextRoll animates characters via CSS transforms — the text string is always in the DOM. When using it inside an interactive element, ensure the parent has a clear accessible label if the visible text may be ambiguous mid-animation. The bounce parameter controls personality, not speed; total animation time is duration + (text.length × stagger) in milliseconds. When prefers-reduced-motion: reduce is set, the component skips the roll animation via useReducedMotion() and renders the text directly — no extra configuration needed.
Credits
Inspired by slot-text.
Built by Axyl. A motion-first component registry for React.
Last updated: 7/26/2026