Create a text typing effect with React

The typing animation effect is a very popular effect on the web. It can be used to create a chat, a loading animation, or just to add a bit of dynamism to your website.

Since OpenAI has released ChatGPT (already almost a year!), we can see this effect more than ever. For a project, I had to create a similar effect, and I thought it would be a good idea to share it with you.

The effect

The code

For this effect, we will use a custom hook:

useTypingEffect.tsx

const useTypingEffect = (
text: string,
duration: number,
isTypeByLetter = false
) => {
const [currentPosition, setCurrentPosition] = useState(0);
const items = isTypeByLetter ? text.split("") : text.split(" ");
useEffect(() => {
setCurrentPosition(0);
}, [text]);
useEffect(() => {
if (currentPosition >= items.length) return;
const intervalId = setInterval(() => {
setCurrentPosition((prevPosition) => prevPosition + 1);
}, duration);
return () => {
clearInterval(intervalId);
};
}, [currentPosition, items, duration]);
return items.slice(0, currentPosition).join(isTypeByLetter ? "" : " ");
};

We have 3 parameters: the text to display, the duration between each letter/word, and a boolean to know if we want to type by letter or by word (I use this in the next section).

In our example below we use the hook like with a texts array, and we change the text every 5 seconds, like this:

TypingEffect.tsx

import useTypingEffect from "./useTypingEffect";
const texts = [
"This is a simple text typing effect in React",
"This effect is created using React Hooks",
"We can use this effect to create a typing effect for our portfolio",
"We can also use this effect to create a typing effect for our resume",
"or for your blog",
"or for your landing page",
"let's go",
];
type TextTypingEffectProps = {
isTypeByLetter?: boolean;
duration?: number;
};
export const TextTypingEffectWithTexts: React.FC<TextTypingEffectProps> = ({
isTypeByLetter = false,
duration = 200,
}) => {
const [textIndex, setTextIndex] = React.useState(0);
const textToShow = useTypingEffect(
texts[textIndex],
duration,
isTypeByLetter
);
React.useEffect(() => {
const intervalId = setInterval(() => {
setTextIndex((prevIndex) =>
prevIndex >= texts.length - 1 ? 0 : prevIndex + 1
);
}, 5000);
return () => {
clearInterval(intervalId);
};
}, []);
return (
<span className="text-black dark:text-white" key={textIndex}>
{textToShow}
</span>
);
};

More

This is very simple hook, we can improve it a bit in term of animation. For example, we can take inspiration from chat.openai, and add a fade out effect and a cursor.

TypingEffect.tsx

// ...
const TIME_TO_FADE = 300;
const TIME_INTERVAL = 3000;
const TIME_PER_LETTER = 100;
export const TextTypingEffectWithTextsFadeOut = () => {
const [textIndex, setTextIndex] = React.useState(0);
const [fadeText, setFadeText] = React.useState(true);
const [fadeCircle, setFadeCircle] = React.useState(true);
const textToShow = useTypingEffect(texts[textIndex], TIME_PER_LETTER, false);
const timeToTypeText = texts[textIndex].split(" ").length * TIME_PER_LETTER;
React.useEffect(() => {
const circleTimeout = setTimeout(() => {
setFadeCircle(false);
}, timeToTypeText + 1000);
const textTimeout = setTimeout(() => {
setFadeText(false);
setTimeout(() => {
setTextIndex((prevIndex) =>
prevIndex >= texts.length - 1 ? 0 : prevIndex + 1
);
setFadeCircle(true);
setFadeText(true);
}, TIME_TO_FADE);
}, TIME_INTERVAL);
return () => {
clearTimeout(circleTimeout);
clearTimeout(textTimeout);
};
}, [textIndex]);
return (
<>
<span
className={`inline-flex items-center text-black duration-300 dark:text-white ${
fadeText ? "opacity-1 translate-y-0" : "translate-y-2 opacity-0"
}`}
key={textIndex}
>
{textToShow}{" "}
<div
className={`ml-2 h-3 w-3 rounded-full bg-black duration-300 dark:bg-white ${
fadeCircle ? "" : "h-0 w-0 opacity-0"
}`}
/>
</span>
</>
);
};

Published: 9/12/2023

Join my newsletter to stay updated about the latest things I've created and discover the great finds I've come across on the internet.