How to create a typewriter effect in vanilla JavaScript (using only HTML CSS and JS)

Reading Time: 8 minutes


  • Introduction
  • Showcase Project
  • HTML
  • CSS
  • JS
  • Code (come here if you’re just looking for the source code)
  • Closure


In today’s tutorial we want to take a quick look on how to create a cool-looking typewriter effect in plain vanilla javascript. We will only use basic HTML, CSS with an animation and some simple JavaScript. No need for any third-party plugins or libraries.

Make sure to watch the video on our Youtube-Channel if you like to 😉

Showcase project

Let’s take a look at what we’re going to build first! As you can see we decided to go for a simple page layout, with a black background and some text centered vertically and horizontally as well.

Plain javascript typewriter effect

The text looks like it gets written by someone typing it in real time and then deleting it again to type the next word. There also is this blinking cursor right next to the text, which will be built using CSS and a custom animation.

Write HTML

Let’s start with creating the project. Create a new folder first. Inside this folder, create 3 files – an index.html file, a CSS file and the javascript file. Name them whatever you want, we chose style.css and app.js.

Now go into the HTML file first. We won’t need much markup for this project. Create a basic document structure. If you’re using VS Code or another editor with the emmet extension enabled, just enter an exclamation mark and hit tab to let emmet create the basic structure for you.

Next, change the title of the page and link the stylesheet in the head of the document. Inside the body, make sure to link the javascript file using the script tag with the source attribute.

Above the script tags inside the body, create a div of class hero. This will be our main wrapper element. Inside there we need an h1 with the class of title. Inside there, fill in the text you want to be displayed permanently on your page. Just keep in mind that you need to insert a space at the end, otherwise the text from the typewriter effect would be too close to the title text.

To do this, you can use a special character which is a bit less known. It’s called ZeroWidthSpace. Just insert it at the end of the heading and your good to go.

The last thing we need in our markup is the element which will contain the word of the typewriter effect.

For this, create another h1 element and give it the class of typewrite. It shouldn’t have any content.

That’s all HTML we have to write for this tutorial. To take a look at what we built so far, open the file with the VS Code live server extension. Alternatively, you can just open the HTML file normally in your browser.

Write CSS

Now that the HTML is complete, we want to go to the styling. Head into your CSS file.

Firstly, select the whole body tag and remove the default padding and margin, by setting both properties to 0. Also change the font-family to a different font. We chose the Courier New font with the normal Courier and monospace font family as a backup. We decided to go with this font, because it looks similar to the font that those old typewriter machines produced, but feel free to choose a different font if you wish.

Next, select the hero class. We want to display it as flex, so the 2 h1s will be displayed next to each other and we can center them easily. For this, set the align-items and the justify-content properties to center.

Then, set the height of the container to 100vh, so it will cover the whole screen. Afterwards, give it a background color of black and a color of white.

As you can see by now, the text is centered on the screen, but it’s a little bit small. To change this, select the h1 element and give it a font-size of 4rem.

Now it looks better.

The last thing to do now is the blinking cursor effect, as the actual writing of the letters will be handled in the JavaScript code.

First select the typewrite class and set its position to relative. Then, select the ::after pseudo-element. This will be used as our blinking cursor.

Set its content to an empty string and give it a width of just one pixel. The actual cursor will just be a border with a simple animation. Therefore, use the border-right property and set it to 5px solid white.

Also, make sure to set the height to 100%.

The border is a little bit too close to the text that will be displayed by our typewriter effect. You don’t see it yet, but it will happen. So, To solve this, just apply some margin on the left. We found 0.5 rem to be a good number.

Now only the animation is left. To create the animation create a new @keyframes query and give it the name of blink. Inside there we just want to set the opacity to 0, when the animation starts, which means at the 0% mark, and set the opacity to 1 again when the animation ends, so at the 100% mark.

Lastly, we need to implement this animation, so go inside the after pseudo-element again and set the animation property to the blink animation 1s infinite, so it will repeat forever. Feel free to experiment with the duration of the animation, we just found 1s to be the best.

If we now take a look at our page in the browser, we can already see the blinking cursor, which means that everything works as it should.

Write JS

We’re almost done. We only have to write the JavaScript code that deals with the writing and deleting of the letters of the words.

So, head inside the javascript file we created earlier. To make this code re-usable in other applications, we won’t declare any global variables. Therefore, we need a function that wraps all of the code and variables we’re going to create.

So, make sure to create a function first. We called it typeWriterEffect and gave it no parameters.

Inside this function, we first need to declare some variables. The first variable should be a const. It is the array that stores all the different words that should be displayed on the screen. Its values are all normal strings. You can add as much as you like to, but we figured that 5 is enough.

After that, we will need two variables that work as our counters for the index of the words array and the index of the letter of the word we’re currently at. Therefore, create a variable called wordcount and one called letterCount and initialize both with 0.

Please note, that these variables, and the ones that will follow, all need to be either let or var, so we can change them later on.

Next, we need two variables that hold strings. One called currentText that will hold the current text displayed on the screen and one called currentWord which contains the current word that is getting built up or deleted. Initialize both of them with an empty string.

We also need a variable that stores the amount of time we want for the timeout. The timeout will be called at the end of each iteration and its duration has to change, for example, when typing the text, it should be slower than when deleting it. But more about that later. For now, just create a variable called timeOut and initialize it with 400.

Lastly, we will need a Boolean that indicates whether we’re currently writing or deleting the word. Therefore, create a variable called isDeleting and set it to false initially.

Now we can start working on the actual type effect. For this, create a new function inside of the one we’re currently in called type. It also takes no parameters.

Inside this function, we first have to check what word we’re currently at. If our wordCount is equal to the length of the words array, that means that we reached the end of the array and have to begin with the first element again. Therefore, create an if statement that checks for this condition, and if it’s true, set the wordCount variable back to 0.

After that, we need the current word. Assign the currentWord variable to the element of the words array the wordCount points at.

 Next, we want to actually build up the word or delete it step by step, letter for letter.

We first have to check whether we’re deleting the word or writing it. Use an if statement for this checking the isDeleting variable. If that’s true, which means that we’re in fact deleting letters, set the currentText variable to the currentWord, but slice it from the beginning, so index 0, to the letterCount minus one.

If we’re building up the word, so the isDeleting variable is false, do the same thing. But instead of decreasing the letterCount, increment it by one.

After that, we have to update the text content of the actual HTML element. Use the document queryselector and select the typewrite class. Then set its textContent attribute to the currentText variable.

Next up we want to change the speed of the effect, which means the duration of the timeout depending on if we’re writing or deleting. For this, use the ternary operator and assign the timeOut variable either to 200 if isDeleting is true or else to 400.

Lastly, we have to check if we finished writing or deleting a word and act accordingly. Use an if statement and check for the following conditions. If we’re writing the word, which means isDeleting is false, AND the currentText is as long as the currentWord, we know that we just finished writing the word.

Therefore, we want to set the timeOut to 2000, so the whole word will be displayed for 2 seconds before we start deleting it. We also want to start the deleting process, so set isDeleting to true.

The other condition we want to check for is the following. If we’re deleting, so isDeleting is set to true AND the currentText has a length of 0, that means that we just finished deleting a word. Use an else if to check for this.

Inside this else if, first set the timeout to 1000, so we won’t immediately start writing the next word. Also, set isDeleting to false, so we can write the next word and increment the wordCount by one.

Outside of this control block but still inside the type() function, call a setTimeout. Give it the type() function as the first and the timeOut variable as the second argument.

Therefore, we ensure that the type() function will run recursively forever.

We’re almost done! As you can see by now, we still see nothing on the screen. That’s because we don’t have called the functions yet.

At the bottom of the typeWriterEffect() function, make sure that it’s outside of the type() function itself, call the type() function once.

Now, at the bottom of the file, outside of the typeWriterEffect() function, invoke the typeWriterEffect() function once, to start the effect.

If you followed along and done everything correctly, you should see the working result now!

Feel free to experiment with the specific values for the timeOut variable to find the perfect configuration for you.



<!DOCTYPE html>
<html lang="en">
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Typewriter Tutorial</title>
    <link rel="stylesheet" href="./style.css" />
    <div class="hero">
      <h1 class="title">Code Student is for &ZeroWidthSpace;</h1>
      <h1 class="typewrite"></h1>
    <script src="./app.js"></script>


body {
  margin: 0;
  padding: 0;
  font-family: 'Courier New', Courier, monospace;

.hero {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100vh;
  background: black;
  color: white;

h1 {
  font-size: 4rem;

.typewrite {
  position: relative;

.typewrite::after {
  content: '';
  width: 1px;
  border-right: 5px solid white;
  height: 100%;
  margin-left: 0.5rem;
  animation: blink 1s infinite;

@keyframes blink {
  0% {
    opacity: 0;
  100% {
    opacity: 1;


function typeWriterEffect() {
  //declare variables

  const words = [

  let wordCount = 0;
  let letterCount = 0;

  let currentText = '';
  let currentWord = '';

  let timeOut = 400;

  let isDeleting = false;

  // actual type effect

  function type() {
    if (wordCount === words.length) {
      wordCount = 0;

    currentWord = words[wordCount];

    if (isDeleting) {
      currentText = currentWord.slice(0, --letterCount);
    } else {
      currentText = currentWord.slice(0, ++letterCount);

    document.querySelector('.typewrite').textContent = currentText;

    timeOut = isDeleting ? 200 : 400;

    if (!isDeleting && currentText.length === currentWord.length) {
      timeOut = 2000;
      isDeleting = true;
    } else if (isDeleting && currentText.length === 0) {
      timeOut = 1000;
      isDeleting = false;
    setTimeout(type, timeOut);




That’s all for today’s tutorial! We just built a cool typewriter effect for your next web project using only HTML, CSS, and plain vanilla JavaScript!

We hope you enjoyed reading this tutorial and learned something new. If you did, please subscribe to our channel for more weekly videos.

Do you want to see more tutorials or even have a specific topic you want us to do a video/blog post about? Let us know it in the comments!

Also make sure to comment down below if you have any questions or critique whatsoever.

We’re excited to see you again next week and until then, stay healthy and keep on learning.

Another post you might be interested in.

Leave a Reply

Your email address will not be published. Required fields are marked *