TypewriterEffect

Text generates as if it is being typed on the screen.


Usage

Code

import { motion, stagger, useAnimate, useInView } from "framer-motion";
import { useEffect } from "react";
import { cnBase } from "tailwind-variants";
export interface TypewriterEffectProps {
words: {
text: string;
className?: string;
}[];
className?: string;
cursorClassName?: string;
}
export const TypewriterEffect: React.FC<TypewriterEffectProps> = ({
words,
className,
cursorClassName,
}) => {
// split text inside of words into array of characters
const wordsArray = words.map((word) => {
return {
...word,
text: word.text.split(""),
};
});
const [scope, animate] = useAnimate();
const isInView = useInView(scope);
useEffect(() => {
if (isInView) {
animate(
"span",
{
display: "inline-block",
opacity: 1,
width: "fit-content",
},
{
duration: 0.3,
delay: stagger(0.1),
ease: "easeInOut",
}
);
}
}, [isInView]);
const renderWords = () => {
return (
<motion.div ref={scope} className="inline">
{wordsArray.map((word, idx) => {
return (
<div key={`word-${idx}`} className="inline-block">
{word.text.map((char, index) => (
<motion.span
initial={{}}
key={`char-${index}`}
className={cnBase(
`dark:text-white text-black opacity-0 hidden`,
word.className
)}
>
{char}
</motion.span>
))}
&nbsp;
</div>
);
})}
</motion.div>
);
};
return (
<div
className={cnBase(
"text-base sm:text-xl md:text-3xl lg:text-5xl font-bold text-center",
className
)}
>
{renderWords()}
<motion.span
initial={{
opacity: 0,
}}
animate={{
opacity: 1,
}}
transition={{
duration: 0.8,
repeat: Infinity,
repeatType: "reverse",
}}
className={cnBase(
"inline-block rounded-sm w-[4px] h-4 md:h-6 lg:h-10 bg-blue-500",
cursorClassName
)}
></motion.span>
</div>
);
};

Props

AttributeTypeDescriptionDefault
childrenReact.ReactNodeThe content to be rendered within the gradient background.undefined
classNamestringThe CSS class to be applied to the inner div.undefined
containerClassNamestringThe CSS class to be applied to the outermost div.undefined
animatestringColor of the avatarstrue