How to Create a Typewriter Effect in Vue.js

Its pretty fun to create little projects in Vue. Ever since I have started learning it, I’ve been hooked and have tried to built numerous things, such as the Seive or Eratosthenes animation, a wordle game, and a few tiny programs here and there.

In the same sequence, this time I have tried to emulate the typewriter effect where text appears as if being typed manually by a typist.

This is what I’ve built and what I’ll teach you to build in this article:

We have an input textbox where a user can type any text. User then can either press Enter or press the button to start the typing effect.

Let’s learn how to build it using Vue.

Set up

We’ll break this program in two parts: in the first part, I will explain how to build the typing effect with the predefined text and in the second part, I’ll explain how to create the input text and connect it so that whatever the user types is then used for the typing effect.

Here’s the starter html template for the first part of this project:

JavaScript
<div id="container">
	<h4>
		{{ text }}
	</h4>
</div>

And here are the CSS styles for this project (for the sake of completeness):

CSS
<style lang="scss">
#container {
	font-family: Avenir, Helvetica, Arial, sans-serif;
	font-family: system-ui;
	line-height: 1.5;
	color: #2c3e50;
	padding: 40px;
	margin: 60px auto;
}

h4 {
	margin: 40px;
}

input {
	margin: 20px;
	width: 200px;
	height: 20px;
	padding: 10px;
	border-radius: 5px;
	border: 1px solid #ccc;
}
</style>

Also read: How to Lighten or Darken a Color with Pure CSS

Creating the typing effect in Vue

In the typewriter effect, we have some text, in which a character is added after a certain duration. This repeats until all the characters in the target text have been appended to the text being “typed”.

Thus, we should have at least two data properties, one for the target text which holds a predefined text and another for the text being typed. Also, we should have a property so that we can define how much delay we want between the successive characters.

It’s clear that to time the appending of characters after certain delay, we will need to use the setTimeout function and in its second argument, (for the duration) we will have to put in the increasing values for each successive character. For this, we will need a counter variable which can keep track of which character is being appended.

Also read: 4 Awesome tricks to Change the Default Fonts in Excalidraw

This is the Vue code that declares these properties and adds a method which will start the typing effect:

JavaScript
export default {
	data() {
		return {
// 		target text
			mainText: "A QUICK BROWN FOX JUMPS OVER THE LAZY DOG!",
			text: "", // dynamically typed text
			counter: 0, 
			delay: 100,
		};
	},
	methods: {
		startTypingEffect() {
			// this.resetData();

			for (let i = 0; i < this.mainText.length; i++) {
				setTimeout(() => {
				// append next character
					this.text = this.text + this.mainText[i];
				}, this.delay * this.counter); // equal delay between characters
				
	  		this.counter += 1;
		}
	},
}

We have the code in place. To fire this method, we can put it inside the mounted hook, like this:

JavaScript
// rest of the code above
mounted() {
  this.startTypingEffect();
}

This will automatically launch the typing effect when the DOM has loaded.

So far, the program works for the text that we have already predefined. We can make this text dynamic by allowing the user to input his preferred text.

Making the target text dynamic

For this purpose, we will create an input element, and tie it’s value to the mainText data property using the v-model directive provided by Vue. I have also created a button element to its side, clicking on which will also launch this effect.

Here’s the complete HTML code incorporating these changes:

JavaScript
<div id="container">
		<label>Input text:</label>
		<input type="text" v-model="mainText" @keyup.enter="startTypingEffect" />
		<button @click="startTypingEffect">
		  Start typing effect 
		</button>
		<h4>
			{{ text }}
		</h4>
</div>

As you can see, I have tied the method startTypingEffect to the click event listener on the button and to the enter key up event on the input textbox. Also, the value of the input text box is now dynamically bound to the mainText data property.

Also read: How to Read the New Yorker and Atlantic articles for FREE

Our code is almost complete, but there’s just one problem. If the user restarts the typing effect, we want the data properties such as counter and text to be reset so that every iteration starts from scratch.

Therefore, we will have to create a resetData function:

JavaScript
resetData() {
	this.text = "";
	this.counter = 0;
}

And we will call this resetData function every time the startTypingEffect function is called. This is the modified code:

JavaScript
startTypingEffect(){
  this.resetData();
  
  // remaining code here
}

and now our code is complete. This is the full version in a Codepen:

<!-- Use preprocessors via the lang attribute! e.g. <template lang="pug"> -->
<template>
	<div id="container">
		<lable>Input text:</lable>
		<input type="text" v-model="mainText" @keyup.enter="startTypingEffect" />
		<button @click="startTypingEffect">Start typing effect</button>
		<h4>
			{{ text }}
		</h4>
	</div>
</template>

<script>
export default {
	data() {
		return {
// 		target text
			mainText: "A QUICK BROWN FOX JUMPS OVER THE LAZY DOG!",
			text: "", // dynamically typed text
			counter: 0, 
			delay: 100,
		};
	},
	methods: {
		startTypingEffect() {
			this.resetData();

			for (let i = 0; i < this.mainText.length; i++) {
				setTimeout(() => {
					this.text = this.text + this.mainText[i];
				}, this.delay * this.counter);
				this.counter += 1;
			}
		},

		resetData() {
			this.text = "";
			this.counter = 0;
		}
	},
	mounted() {
		// 		start on DOM loading
		this.startTypingEffect();
	}
};
</script>

<!-- Use preprocessors via the lang attribute! e.g. <style lang="scss"> -->
<style lang="scss">
#container {
	font-family: Avenir, Helvetica, Arial, sans-serif;
	font-family: system-ui;
	line-height: 1.5;
	color: #2c3e50;
	padding: 40px;
	margin: 60px auto;
}

h4 {
	margin: 40px;
}

input {
	margin: 20px;
	width: 200px;
	height: 20px;
	padding: 10px;
	border-radius: 5px;
	border: 1px solid #ccc;
}
</style>

Further Suggestions

Our basic program is complete but there are a lot of things you can try to improve:

  1. If the user presses the restart button before the previous iteration of effect has finished, the output becomes garbled. That’s because of delayed execution due to setTimeout functions. You can try to implement a resetTimeouts function which will clear all the previous setTimeouts to prevent them from executing in future cycles.
  2. You can include a speed slider (slow, medium, fast) which allows user to adjust the speed of the animation.

Wrap up

That’s it guys. Hopefully, you liked the tutorial. Once you learn the basics, building stuff with Vue is a fun and rewarding experience.

Thanks for reading. You can follow me on Twitter.