ComponentsText Animations
Gooey Text Morphing
Liquid gooey text morph effect for React. SVG filter-based animation with smooth blob-like transitions. Unique eye-catching text animation.
Last updated on
Gooey Text Morphing
"use client";
import { GooeyText } from "@/components/ui/gooey-text-morphing";
export function GooeyTextMorphingDemo() {
const texts = ["Why", "is", "it", "so", "satisfying", "to", "watch?"];
return (
<div className="flex h-[300px] w-full items-center justify-center overflow-hidden rounded-xl border bg-background">
<GooeyText
texts={texts}
morphTime={1}
cooldownTime={0.5}
textClassName="font-bold tracking-tighter"
/>
</div>
);
}Installation
CLI
npx shadcn@latest add "https://jolyui.dev/r/gooey-text-morphing"Manual
Copy and paste the following code into your project. component/ui/gooey-text-morphing.tsx
"use client";
import * as React from "react";
import { cn } from "@/lib/utils";
interface GooeyTextProps {
texts: string[];
morphTime?: number;
cooldownTime?: number;
className?: string;
textClassName?: string;
}
export function GooeyText({
texts,
morphTime = 1,
cooldownTime = 0.25,
className,
textClassName,
}: GooeyTextProps) {
const text1Ref = React.useRef<HTMLSpanElement>(null);
const text2Ref = React.useRef<HTMLSpanElement>(null);
React.useEffect(() => {
let textIndex = texts.length - 1;
let time = new Date();
let morph = 0;
let cooldown = cooldownTime;
const setMorph = (fraction: number) => {
if (text1Ref.current && text2Ref.current) {
text2Ref.current.style.filter = `blur(${Math.min(8 / fraction - 8, 100)}px)`;
text2Ref.current.style.opacity = `${fraction ** 0.4 * 100}%`;
fraction = 1 - fraction;
text1Ref.current.style.filter = `blur(${Math.min(8 / fraction - 8, 100)}px)`;
text1Ref.current.style.opacity = `${fraction ** 0.4 * 100}%`;
}
};
const doCooldown = () => {
morph = 0;
if (text1Ref.current && text2Ref.current) {
text2Ref.current.style.filter = "";
text2Ref.current.style.opacity = "100%";
text1Ref.current.style.filter = "";
text1Ref.current.style.opacity = "0%";
}
};
const doMorph = () => {
morph -= cooldown;
cooldown = 0;
let fraction = morph / morphTime;
if (fraction > 1) {
cooldown = cooldownTime;
fraction = 1;
}
setMorph(fraction);
};
function animate() {
requestAnimationFrame(animate);
const newTime = new Date();
const shouldIncrementIndex = cooldown > 0;
const dt = (newTime.getTime() - time.getTime()) / 1000;
time = newTime;
cooldown -= dt;
if (cooldown <= 0) {
if (shouldIncrementIndex) {
textIndex = (textIndex + 1) % texts.length;
if (text1Ref.current && text2Ref.current) {
text1Ref.current.textContent =
texts[textIndex % texts.length] ?? "";
text2Ref.current.textContent =
texts[(textIndex + 1) % texts.length] ?? "";
}
}
doMorph();
} else {
doCooldown();
}
}
animate();
return () => {
// Cleanup function if needed
};
}, [texts, morphTime, cooldownTime]);
return (
<div className={cn("relative", className)}>
<svg className="absolute h-0 w-0" aria-hidden="true" focusable="false">
<defs>
<filter id="threshold">
<feColorMatrix
in="SourceGraphic"
type="matrix"
values="1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 255 -140"
/>
</filter>
</defs>
</svg>
<div
className="flex items-center justify-center"
style={{ filter: "url(#threshold)" }}
>
<span
ref={text1Ref}
className={cn(
"absolute inline-block select-none text-center text-6xl md:text-[60pt]",
"text-foreground",
textClassName,
)}
/>
<span
ref={text2Ref}
className={cn(
"absolute inline-block select-none text-center text-6xl md:text-[60pt]",
"text-foreground",
textClassName,
)}
/>
</div>
</div>
);
}Usage
import { GooeyText } from "@/components/ui/gooey-text-morphing";
function MyComponent() {
const texts = ["Hello", "World", "Gooey", "Morph"];
return (
<GooeyText
texts={texts}
morphTime={1}
cooldownTime={0.5}
/>
);
}API Reference
Prop
Type
Notes
- The component uses SVG filters to achieve the "gooey" effect.
- The
textsarray can contain any number of strings. morphTimecontrols the duration of the transition between words.cooldownTimecontrols how long each word stays visible before morphing again.- Ensure the container has enough height to accommodate the text size.
Credits
Inspired by Victor Welander.
How is this guide?
Glitch Text
Cyberpunk-style glitch text effect for React. Digital distortion animation with character scrambling. Perfect for hero sections and tech websites.
Highlight Text
Animated text highlighter for React. SVG underlines, boxes, circles, and marker effects. Draw attention to key words with hand-drawn style animations.