Text Loop
Text animation that transitions between multiple items, creating an engaging looping effect.
Text Loop with custom variants and transition
Show code
Installation
Copy and paste the following code into your project.
"use client";
import { cn } from "@/lib/utils";
import {
AnimatePresence,
type Transition,
type Variants,
motion,
} from "motion/react";
import { Children, useEffect, useState } from "react";
type TextLoopProps = {
children: React.ReactNode[];
className?: string;
interval?: number;
transition?: Transition;
variants?: Variants;
onIndexChange?: (index: number) => void;
};
export default function TextLoop({
children,
className,
interval = 2,
transition = { duration: 0.3 },
variants,
onIndexChange,
}: TextLoopProps) {
const [currentIndex, setCurrentIndex] = useState(0);
const items = Children.toArray(children);
useEffect(() => {
const intervalMs = interval * 1000;
const timer = setInterval(() => {
setCurrentIndex((current) => {
const next = (current + 1) % items.length;
onIndexChange?.(next);
return next;
});
}, intervalMs);
return () => clearInterval(timer);
}, [items.length, interval, onIndexChange]);
const motionVariants: Variants = {
initial: { y: 20, opacity: 0 },
animate: { y: 0, opacity: 1 },
exit: { y: -20, opacity: 0 },
};
return (
<div className={cn("relative inline-block whitespace-nowrap", className)}>
<AnimatePresence mode="popLayout" initial={false}>
<motion.div
key={currentIndex}
initial="initial"
animate="animate"
exit="exit"
transition={transition}
variants={variants || motionVariants}
>
{items[currentIndex]}
</motion.div>
</AnimatePresence>
</div>
);
}
"use client";
import { cn } from "@/lib/utils";
import {
AnimatePresence,
type Transition,
type Variants,
motion,
} from "motion/react";
import { Children, useEffect, useState } from "react";
type TextLoopProps = {
children: React.ReactNode[];
className?: string;
interval?: number;
transition?: Transition;
variants?: Variants;
onIndexChange?: (index: number) => void;
};
export default function TextLoop({
children,
className,
interval = 2,
transition = { duration: 0.3 },
variants,
onIndexChange,
}: TextLoopProps) {
const [currentIndex, setCurrentIndex] = useState(0);
const items = Children.toArray(children);
useEffect(() => {
const intervalMs = interval * 1000;
const timer = setInterval(() => {
setCurrentIndex((current) => {
const next = (current + 1) % items.length;
onIndexChange?.(next);
return next;
});
}, intervalMs);
return () => clearInterval(timer);
}, [items.length, interval, onIndexChange]);
const motionVariants: Variants = {
initial: { y: 20, opacity: 0 },
animate: { y: 0, opacity: 1 },
exit: { y: -20, opacity: 0 },
};
return (
<div className={cn("relative inline-block whitespace-nowrap", className)}>
<AnimatePresence mode="popLayout" initial={false}>
<motion.div
key={currentIndex}
initial="initial"
animate="animate"
exit="exit"
transition={transition}
variants={variants || motionVariants}
>
{items[currentIndex]}
</motion.div>
</AnimatePresence>
</div>
);
}
Update the import paths to match your project setup.
Props
Prop | Type | Default |
---|---|---|
children | React.ReactNode[] | - |
className | string | - |
interval | number | 2000 |
transition | Transition | - |
variant | Variants | - |
onIndexChange | (index: number) => void | - |
Edit on GitHub
Last updated on