slot-text: a zero-dependency text roll animation for Copy buttons and status labels
Dependency-free text roll animation for vanilla JS, React, and Vue.
At a glance
- What is it?
- slot-text is an MIT-licensed TypeScript library that animates short UI labels by rolling characters or words in and out. It ships vanilla, React, Vue, Solid and Svelte entry points, and it has one interesting trick: a flash() call that reverts itself.
- Who is it for?
- Adopt slot-text if you need short, self-contained label animation in a project that already uses React 18 or 19, Vue 3.4+, Solid 1.8+ or Svelte 4 or 5, and if you accept the character-mode trade-offs the README lists. Do not adopt it for long text, paragraphs, joined scripts such as Arabic or Devanagari, or anything where kerning and ligatures matter, because character mode breaks them by design.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 39 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 18, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The narrow problem slot-text was built for
Most animation libraries are general-purpose. slot-text is not. It targets one shape of interface element: a short label that changes state, such as a Copy button that becomes Copied, a status chip that moves from Idle to Running, or a counter that ticks. The README describes the scope as "tiny, tactile UI labels," and every design decision follows from that.
The intended user is a frontend developer who wants the roll effect without pulling in an animation runtime. The package has zero runtime dependencies, and React, Vue, Solid and Svelte are listed as optional peer dependencies, so a plain JavaScript page does not need any of them. If your label is a sentence, or your animation is a page transition, this is the wrong tool and the README does not pretend otherwise.
There is a second, quieter audience: people building spam-prone buttons. The flash() method exists specifically for the Copy to Copied to Copy cycle, and the documentation calls it "spam-safe."
How the roll works: measured cells, staggered segments
slot-text is a browser-only DOM utility. The exported slotText() function takes an element and an initial string, measures each animatable segment into its own cell, and animates those cells when the text changes. The unit of animation is controlled by rollBy, which defaults to "character" and can be set to "word." In character mode each user-perceived character gets a measured cell; in word mode each word rolls as a single unit.
Timing is expressed through three numbers. duration is the per-segment animation time and defaults to 300 ms. stagger is the delay between segments and defaults to 45 ms, so a longer string starts later at the tail than at the head. exitOffset, default 50 ms, delays the outgoing character before it leaves. Lower values make the whole roll faster. The easing defaults to a springy bezier and bounce defaults to 0.6, which is where the overshoot comes from.
Two options change the semantics rather than the look. skipUnchanged, true by default, avoids re-rolling characters that are identical between the old and new strings. interrupt, also true by default, cuts off a roll in flight and starts fresh. Setting interrupt to false lets the current roll finish and plays the latest call after it lands, dropping duplicates. That second mode is the one the README recommends for spam-prone buttons.
The colour system is per-segment. color accepts a string or a function of (index, total) returning a string, and colorFade, default 280 ms, fades back to the base colour. The bundled chromatic() helper is a rainbow sweep built on that hook. The package also exports lower-level pieces: buildSlotText, animateSlotText and chromatic.
Installing slot-text and getting a first flash working
The README lists npm, pnpm, Bun and Yarn as install routes. Any of them works; the package publishes as an ES module with type declarations.
npm install slot-textAfter installing, import the stylesheet once and attach the helper to an element. The README's quick start uses a Copy button and the chromatic() colour helper for the roll-in.
import "slot-text/style.css";
import { slotText, chromatic } from "slot-text";
const label = slotText(document.querySelector("#copy")!, "Copy");
label.flash("Copied", { enter: { color: chromatic() } });What you should see: the label rolls from Copy to Copied, then reverts on its own after the default 1400 ms. The stylesheet import is not optional. The README states that if the stylesheet loads late, labels stay as plain readable text and upgrade to slot animation once the required layout styles become available, so a missing import degrades quietly rather than throwing.
To try the shipped example locally, the README gives a build followed by a static server.
npm run build
python3 -m http.serverOpen http://localhost:8000/examples/basic/ and you get a playground with one button that flashes Copy, Copied, Copy.
flash() versus set(), and why the distinction matters
The API surface is three methods: set, flash and destroy. set(text, options?) rolls to new text and the text stays. flash(text, options?) rolls in and then auto-reverts to the previous text. destroy() cleans up.
The revert behaviour is the part worth reading carefully. The README states that repeated clicks restart the revert timer instead of queuing extra rolls, and that an explicit set() cancels any pending revert. That combination is what makes the method usable on a button a user might hammer. Without it, ten fast clicks would be ten queued animations.
flash accepts a revertAfter option in milliseconds, defaulting to 1400, plus separate enter and exit option objects. The example in the README sets enter to roll up with a chromatic colour and exit to roll down, which gives the return trip a different direction from the arrival.
set accepts the same option shape, including rollBy and stagger, so a permanent change can be made faster and word-by-word rather than character-by-character. The README's example passes rollBy: "word", duration: 180 and stagger: 25 together for that effect. Note that these are per-call options, not global configuration; there is no documented global defaults object.
Framework wrappers and the CSS import they all share
The package exposes five entry points: the root, plus ./react, ./vue, ./solid and ./svelte. Each has its own type declarations. The React and Vue wrappers are components named SlotText, taking a text prop and an options prop. The Solid and Svelte integrations are directives instead: use:slotText in both cases, receiving an object with text and options.
The asymmetry is deliberate, not an oversight. React and Vue have a natural component model for this, and Solid and Svelte have directive syntax that avoids re-rendering a component on every label change. It does mean the mental model differs depending on your framework, and the README does not explain the choice.
Every one of the five examples imports "slot-text/style.css" first. The package.json marks that file under sideEffects, which is the standard signal that the stylesheet must not be tree-shaken away. If your bundler is configured unusually, that is the field to check.
Peer dependency ranges are explicit: react >=18 <20, solid-js >=1.8 <2, svelte >=4 <6, vue >=3.4 <4. All four are marked optional. React 17 and Vue 2 are outside the supported range.
Where character mode breaks, and when to use word mode
This is the section the README is most honest about, and it is the reason to read before installing. In character mode, each character animates in its own measured cell. That measurement is what enables the effect, and it is also what breaks text shaping.
Four consequences are documented. Kerning is lost, so pairs like AV sit slightly looser; the README calls this invisible at label sizes. Ligatures will not form, including fi, fl and coding ligatures. Joined scripts such as Arabic and Devanagari render as isolated forms. Grapheme clusters, including combining marks and ZWJ emoji, stay together only when Intl.Segmenter is available; older browsers fall back to Unicode code points, which can split them.
There is also a layout constraint: very tall display fonts may clip at the roll mask, which the README attributes to line-height: 1.3. That is a fixed value, not an option in the documented table.
Word mode preserves kerning, ligatures and joined-script shaping inside each word, but the whole word rolls together, so you lose the per-character stagger. For a two-word status label that is usually an acceptable trade. For a single word, word mode reduces to one rolling unit and the effect largely disappears.
The README's own summary is that the library is ideal for short labels, numbers, statuses and commands, in essentially any font you would use for those. That is a narrower claim than the feature list suggests, and it is the right one.
Alternatives: animate.css and GSAP SplitText
The obvious comparison is a CSS animation library such as animate.css. The difference is in who owns the text nodes. animate.css applies a named keyframe animation to an element you already control; you write the markup and the class. slot-text instead measures the string, builds the per-segment cells and drives them, which is why it can stagger a word or skip unchanged characters. If your label never changes, or you are happy to keyframe a whole element, a CSS class is less machinery.
The closer comparison is a text-splitting tool such as GSAP's SplitText, which also breaks a string into animatable units. SplitText splits for you and hands the pieces to an animation engine, so the roll, the revert timer and the interrupt policy are yours to write. slot-text bundles those three behaviours into set, flash and interrupt. The cost is scope: slot-text does not animate anything other than the roll, and there is no timeline, no sequencing across elements and no plugin system. If you need a coordinated sequence of animations, you are looking at the wrong package.
A third option is writing the effect yourself with a mask, a transform and a transitionend listener. That is perhaps sixty lines. The parts you would probably get wrong first are the interrupt policy and the Intl.Segmenter fallback, both of which the README documents here.
Maintenance, licence and what a version bump costs you
The repository is not archived and the last push was on 2026-08-10. The published version in package.json is 0.3.4, and no releases were retrieved for this review, so the changelog history is not something I can describe. A 0.x version number means the maintainer has not committed to API stability, and the README does not document a deprecation policy or a rollback procedure.
Upgrade cost is low on paper. The library has zero runtime dependencies, so there is no transitive tree to audit. The build is a single tsc -p tsconfig.json pass, and the test script is vitest run. The published files list includes dist, examples, style.css and the type declaration for it, so consumers get a small surface.
The real upgrade risk is the peer dependency ranges. React is pinned to >=18 <20, Vue to >=3.4 <4, Svelte to >=4 <6 and Solid to >=1.8 <2. A major version of any of those frameworks can push you outside the supported range even if slot-text itself has not changed.
The licence is MIT, which permits commercial use and modification. That is a statement about the licence text, not legal advice; check how MIT interacts with your own distribution model.
One more cost worth naming: the CSS import is a side effect. It must survive your bundler's tree shaking, and the package marks it under sideEffects to help. If you use a CSS-in-JS pipeline or a strict module graph, verify the stylesheet actually arrives, because the documented failure mode is silent degradation to plain text rather than an error.
Editorial conclusion
Adopt slot-text if you need short, self-contained label animation in a project that already uses React 18 or 19, Vue 3.4+, Solid 1.8+ or Svelte 4 or 5, and if you accept the character-mode trade-offs the README lists. Do not adopt it for long text, paragraphs, joined scripts such as Arabic or Devanagari, or anything where kerning and ligatures matter, because character mode breaks them by design. Before committing, check the browser support of Intl.Segmenter in your target matrix and confirm which of your fonts survive the line-height: 1.3 mask.
Frequently asked questions
What does "slot" mean in slot-text?
It refers to the slot-machine style roll the library produces: each character or word occupies its own measured cell and rolls in or out of it. The README describes the effect as a text roll for short UI labels.
Can you give an example of slot-text in use?
The README's quick start attaches slotText to a Copy button and calls label.flash("Copied", { enter: { color: chromatic() } }), which rolls Copy to Copied and then reverts. The bundled playground has one button that flashes Copy, Copied, Copy.
How do I install slot-text and import its stylesheet?
Run npm install slot-text, then import "slot-text/style.css" once before using the animation. The README states that if the stylesheet loads late, labels stay as plain readable text until the required layout styles become available.
Does slot-text work with React, Vue, Solid and Svelte?
Yes. The package exports ./react, ./vue, ./solid and ./svelte entry points. React and Vue use a SlotText component, while Solid and Svelte use a use:slotText directive, and all four frameworks are optional peer dependencies.
Can slot-text animate long sentences or Arabic text?
The README scopes the library to short labels, numbers, statuses and commands. In character mode, joined scripts such as Arabic and Devanagari render as isolated forms and ligatures do not form; word mode preserves shaping inside each word but rolls the whole word as one unit.
Community notes