Updated
—
4 min read

Photo by Luca Onniboni
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.
Join my newsletter for lessons, experiments, and failures in bootstrapping online businesses.
Sign up if you're curious. I’ll only email you if it's actually good.

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.
What I'm currently building →.