Updated
—
4 min read
Here's a quick tutorial on creating a "typewriter" effect in Svelte.
This is a simple text animation that makes it look like the text is being typed out, one character at a time, then deleted, and so on...
This works well for fancy headings or other text elements that you want to draw attention to.
Here's a GIF of the effect in action you can see what we're building:
I've decided to make this a Svelte use:action
directive, so you can easily apply it to any text element.
export function animateTyping(node: HTMLElement, texts: string[]) {
let textsToType = texts;
let textsIndex = 0;
let charIndex = 0;
let currentText = '';
const updateInnerHTML = (isTyping?: boolean) => {
// Always include the zero-width space and conditionally add the text and cursor
node.innerHTML = '​' + currentText + (isTyping ? '|' : '');
};
const blinkingCursor = () => {
if (currentText.endsWith('|')) {
currentText = currentText.slice(0, -1);
} else {
currentText += '|';
}
updateInnerHTML();
};
// Blinking cursor effect
const waitingEffect = (delay: number) => {
const interval = setInterval(blinkingCursor, 500);
setTimeout(() => {
clearInterval(interval); // Stop blinking
if (currentText.endsWith('|')) {
// Remove cursor if it's still there
currentText = currentText.slice(0, -1);
}
updateInnerHTML();
}, delay);
};
// Typing animation effect
const typeEffect = () => {
const currentString = textsToType[textsIndex];
const delay = currentString[charIndex] === ' ' ? 75 : 100; // Faster delay for spaces
if (charIndex < currentString.length) {
currentText += currentString[charIndex++];
setTimeout(typeEffect, delay);
} else {
waitingEffect(3000);
setTimeout(deleteEffect, 3000); // Wait before starting to delete
}
updateInnerHTML(true);
};
// Deleting animation effect
const deleteEffect = () => {
if (charIndex > 0) {
currentText = currentText.slice(0, --charIndex);
setTimeout(deleteEffect, 50);
} else {
textsIndex = (textsIndex + 1) % textsToType.length;
currentText = ''; // Clear text but keep zero-width space
waitingEffect(3000);
setTimeout(typeEffect, 3000);
}
updateInnerHTML();
};
// Start typing effect
setTimeout(typeEffect, 500);
return {
onDestroy() {},
update(newTexts: string[]) {
textsToType = newTexts;
}
};
}
A Few Notes:
​
) to ensure the text container has a height, even when the text is empty.
Without this, there would be a layout shift when the text is empty and the cursor appears/disappears.|
) that's added and removed from the end of the text stringOnce you have your animateTyping
function, you can use it in your Svelte components like this:
In your Svelte app, you can use the use:animateTyping
and supply a list of strings to type out in sequence.
For example:
<h1>
We offer all kinds of <span use:animateTyping={[
"web development",
"graphic design",
"seo"
]} /> services
</h1>
It's pretty self-explanatory, but here's how it works:
Upon mount, wait 500ms then run typeEffect
typeEffect
is self-calling and types out the current string in textsToType
one character at a time, with a delay
of 100ms (75ms for spaces)
After typing out the string, start the blinking cursor effect (waitingEffect
), wait 3000ms then run deleteEffect
Similarly, deleteEffect
is self-calling and deletes the current string one character at a time, with a delay of
50ms
After deleting the string, show blinking cursor again, and move on to the next string in textsToType
Repeat steps 2-5 indefinitely
Hope you find this helpful, and feel free to use it in your projects!
Let me know if you have any questions or improvements to suggest.
Meet the Author
Ryan Chiang
Hello, I'm Ryan. I build things and write about them. This is my blog of my learnings, tutorials, and whatever else I feel like writing about.
See what I'm building →.
Thanks for reading! If you want a heads up when I write a new blog post, you can subscribe below: