How to Create a Typewriter Effect in Svelte (Fancy Text Animation)

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.

What It Looks Like:

Here's a GIF of the effect in action you can see what we're building:

Svelte Typewriter Effect

The Code

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:

  • I'm using a zero-width space (&#8203;) 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.
  • The blinking cursor is simply a pipe character (|) that's added and removed from the end of the text string

Once you have your animateTyping function, you can use it in your Svelte components like this:

How to Use It

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>

Explanation

It's pretty self-explanatory, but here's how it works:

  1. Upon mount, wait 500ms then run typeEffect

  2. 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)

  3. After typing out the string, start the blinking cursor effect (waitingEffect), wait 3000ms then run deleteEffect

  4. Similarly, deleteEffect is self-calling and deletes the current string one character at a time, with a delay of 50ms

  5. After deleting the string, show blinking cursor again, and move on to the next string in textsToType

  6. Repeat steps 2-5 indefinitely

Wrapping Up

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.

Ryan Chiang

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:

2024

2023

© 2023 Ryan Chiang|ryanschiang.com