Morphing text cross-fades a headline through an ordered list of words, blurring each into the next so they fuse into one gooey shape mid-transition. Watch it below.
Morphing text cross-fades through an ordered list of words, one word at a time, looping back to the first. Each transition blurs the outgoing word out and the incoming word in while a threshold filter clamps the alpha channel, so the two blurred shapes fuse into a single gooey form mid-cycle rather than reading as a soft double-exposure.
Use it for a headline that needs to say more than one thing: a rotating set of verbs, product qualities, or audience labels. It renders in --font-garamond at display size and centers itself in its container.
Import the pattern from loosethreads:
import { MorphingText } from "@pulp/loosethreads/patterns"Pass the words you want to cycle through. Drive enabled from the viewer's motion preference so the morph pauses to a static word when reduced motion is requested:
import { MorphingText } from "@pulp/loosethreads/patterns"
import { useEffect, useState } from "react"
function VerbCycle() {
const [enabled, setEnabled] = useState(true)
// Turn the morph off for viewers who ask for reduced motion.
useEffect(() => {
const query = window.matchMedia("(prefers-reduced-motion: reduce)")
const update = () => setEnabled(!query.matches)
update()
query.addEventListener("change", update)
return () => query.removeEventListener("change", update)
}, [])
return (
<MorphingText
texts={["Extract", "Understand", "Reason", "Decide", "Act"]}
enabled={enabled}
/>
)
}| Prop | Type | Default | Description |
|---|---|---|---|
texts | string[] | Required | Ordered list of words to morph through. The component loops back to the first after the last, and joins the list into the container aria-label so the full set is announced at once. |
enabled | boolean | Required | Runs the morph loop when true. When false the loop is skipped and the first word renders statically. Wire it to the inverse of prefers-reduced-motion. |
Two absolutely-stacked spans hold the outgoing and incoming words. A requestAnimationFrame loop measures real elapsed time and animates each span's filter: blur() and opacity across a short morph window followed by a brief cooldown before the next word. The container applies an SVG feColorMatrix threshold filter, under a component-unique id, that clamps alpha so the two blurred words merge into one shape.
The loop pauses off-screen. While the text is scrolled out of view the RAF loop keeps running but skips the per-frame blur writes, which are the expensive repaint, and resets its clock so the morph does not fast-forward when it returns to view.
aria-label built from the full word list, so assistive tech hears every word rather than trying to track the animated glyphs.prefers-reduced-motion. Pass enabled={false} when reduce is set: the loop is skipped and the first word renders static, with no blur and full opacity.Next: Video text. Back to Text effects.