Home

8 minute read

Create a Typing Effect in AlpineJS

Tony Lea

Create a Typing Effect in AlpineJS

In this tutorial, I'll show you how to use AlpineJS to create a simple typing effect. You can take a look at a quick example of what we'll be creating in the Codepen below:

This can easily be accomplished by adding some sprinkles of AlpineJS to our element. Let's begin.

Creating Our Component

The first thing we'll want to do is to create a simple component that displays some text on the screen:

<div class="w-screen h-screen flex items-center justify-center">
    <div class="max-w-7xl mx-auto text-center"
        x-data="{ text: 'Alpine JS is Amazing' }">

        <h1 class="text-7xl font-black" x-text="text"></h1>

    </div>
</div>

Using the x-text attribute we are simply displaying the value that is stored inside of our x-data="{ text: '' } variable. You can take a look at this simple component displayed in a codepen below:

Next, we'll be adding a few more data variables and create our x-init function to be loaded when our component is rendered.

Initializing the Component

By using the x-init functionality we can run some code as soon as our component has been rendered. This is where we will put our typing functionality for our component:

<div class="w-screen h-screen flex items-center justify-center">
    <div class="max-w-7xl mx-auto text-center"
         x-data="
            { 
                text: '', 
                textArray: ['Alpine JS is Awesome'],
                textIndex: 0,
                charIndex: 0,
                typeSpeed: 1000,
            }"
         x-init="
            setInterval(function(){
                $data.text = $data.textArray[$data.textIndex];
            }, $data.typeSpeed);">
        
        <h1 class="text-7xl font-black" x-text="text"></h1>
        
    </div>
</div>

As you can see above, we have added some more variables to our component data, which are:

Then, if you take a look inside the x-init directive, you'll see that we are looping through an interval of our typeSpeed and setting the text variable equal to the first array value from our textArray.

Now, we have enough data in our component to start creating our text typing effect. Let's do that in the next step.

Creating The Typing Effect

Adding the typing effect is pretty simple. We can increment our current charIndex and display a substring value that we want to be displayed on the screen.

Here is the modified setInterval function that we need in order to implement our typing functionality:

setInterval(function () {
    let current = $data.textArray[$data.textIndex];
    $data.text = current.substring(0, $data.charIndex);
    $data.charIndex += 1;
}, $data.typeSpeed);

All that we are doing is setting our text variable to be a substring from character 0 to the current charIndex, and each time we go over the loop, we increase that index. Here is the full component code:

<div class="w-screen h-screen flex items-center justify-center">
    <div class="max-w-7xl mx-auto text-center"
         x-data="{
                    text: '',
                    textArray : ['Alpine JS is Amazing'],
                    textIndex: 0,
                    charIndex: 0,
                    typeSpeed: 100,
                 }"
         x-init="setInterval(function(){
                    let current = $data.textArray[ $data.textIndex ];
                    $data.text = current.substring(0, $data.charIndex);
                    $data.charIndex += 1;
                 }, $data.typeSpeed);">
        <h1 class="text-7xl font-black" x-text="text"></h1>
    </div>
</div>

You can see a quick example of this below (You may need to click the 'Rerun' button to see the effect)

When we reach the end of the text, it will continue to increment charIndex. An ideal behavior might be to reverse the typing direction after we reach the end of the line.

Let's cover that in the next section.

Reverse The Typing Direction

If we want to reverse the direction of our typing effect, we'll need to add another variable to track the direction of our typing, which can be forward or backward.

Then inside our x-init interval, We'll need to check if we are at the end of the text and set the direction to backward. Here is the updated component to accomplish this:

<div class="w-screen h-screen flex items-center justify-center">
    <div class="max-w-7xl mx-auto text-center"
         x-data="{
                    text: '',
                    textArray : ['Alpine JS is Amazing'],
                    textIndex: 0,
                    charIndex: 0,
                    pause: 1000,
                    typeSpeed: 100,
                    direction: 'forward'
                 }"
         x-init="setInterval(function(){
                    let current = $data.textArray[ $data.textIndex ];
                    if($data.charIndex > current.length){
                         $data.direction = 'backward';
                    }   
                       
                    $data.text = current.substring(0, $data.charIndex);
                    if($data.direction == 'forward'){
                        $data.charIndex += 1;
                     } else {
                        $data.charIndex -= 1;
                        if($data.charIndex == 0){
                            $data.direction = 'forward';
                        }
                     }
                    
                 }, $data.typeSpeed);">
        <h1 class="text-7xl font-black" x-text="text"></h1>
    </div>
</div>

Here is an example of how our typing effect is currently going to function:

Let me break down the updates to the interval we made above. The first update was to check if the current character is greater than the length of the text string, and, if so we set the direction to backward:

if($data.charIndex > current.length){
    $data.direction = 'backward';
}

Next, if we are moving forward we want to increment are charIndex; otherwise, we subtract one:

if ($data.direction == 'forward') {
    $data.charIndex += 1;
} else {
    $data.charIndex -= 1;
    if ($data.charIndex == 0) {
        $data.direction = 'forward';
    }
}

We also do a check to see if the charIndex is equal to zero when moving backward, this way we can change it back to move forward.

Next, we can move on to adding a pause at the end and the start of our typing effect.

Adding Pauses

During our typing effect we may want to added the ability to pause the typing at the end and at the beginning. This will give the effect a more realistic display.

The first thing we are going to do is add two more variables for pauseEnd and pauseStart, like so:

{
    text: '',
    textArray: ['Alpine JS is Amazing'],
    textIndex: 0,
    charIndex: 0,
    pauseEnd: 1000,
    pauseStart: 1000,
    typeSpeed: 100,
    direction: 'forward'
}

Then, inside our x-init we need pause our interval at the end and beginning our text. We can do that with the following code:

let typingInterval = setInterval(startTyping, $data.typeSpeed);

function startTyping() {
    let current = $data.textArray[$data.textIndex];
    if ($data.charIndex > current.length) {
        $data.direction = 'backward';
        clearInterval(typingInterval);
        setTimeout(function () {
            typingInterval = setInterval(startTyping, $data.typeSpeed);
        }, $data.pauseEnd);
    }

    $data.text = current.substring(0, $data.charIndex);
    if ($data.direction == 'forward') {
        $data.charIndex += 1;
    } else {
        if ($data.charIndex == 0) {
            $data.direction = 'forward';
            clearInterval(typingInterval);
            setTimeout(function () {
                typingInterval = setInterval(startTyping, $data.typeSpeed);
            }, $data.pauseStart);
        }
        $data.charIndex -= 1;
    }
}

Breaking this down, you can see that we are simply clearing the interval and starting it back up again at the beginning and at the end:

clearInterval(typingInterval);
setTimeout(function () {
    typingInterval = setInterval(startTyping, $data.typeSpeed);
}, $data.pauseEnd);

Here is how our overall component will look:

<div class="w-screen h-screen flex items-center justify-center">
    <div class="max-w-7xl mx-auto text-center" x-data="{
                    text: '',
                    textArray : ['Alpine JS is Amazing'],
                    textIndex: 0,
                    charIndex: 0,
                    pauseEnd: 1000,
                    pauseStart: 1000,
                    typeSpeed: 100,
                    direction: 'forward'
                 }" x-init="(() => { 
                 
                    let typingInterval = setInterval(startTyping, $data.typeSpeed);
                 
                 function startTyping(){
                    let current = $data.textArray[ $data.textIndex ];
                    if($data.charIndex > current.length){
                         $data.direction = 'backward';
                         clearInterval(typingInterval);
                         setTimeout(function(){
                            typingInterval = setInterval(startTyping, $data.typeSpeed);
                         }, $data.pauseEnd);
                    }   
                       
                    $data.text = current.substring(0, $data.charIndex);
                    if($data.direction == 'forward'){
                        $data.charIndex += 1;
                     } else {
                        if($data.charIndex == 0){
                            $data.direction = 'forward';
                            clearInterval(typingInterval);
                            setTimeout(function(){
                                typingInterval = setInterval(startTyping, $data.typeSpeed);
                            }, $data.pauseStart);
                        }
                        $data.charIndex -= 1;
                     }
                  
                 }
             })()">
        <h1 class="text-7xl font-black" x-text="text"></h1>
    </div>
</div>

Note: we are using a self-invokable function, (() => { //add code here }(), that is executed as soon as the component is initialized.

And here is an example of how our typing effect is currently functioning at the moment:

Looping Through Text Lines

We may want our typing effect to loop through multiple lines of text. We can add a few more lines of text to our textArray variable, and we increment the textIndex each time we finish a line of text.

<div class="w-screen h-screen flex items-center justify-center">
    <div class="max-w-7xl mx-auto text-center" x-data="{
                    text: '',
                    textArray : ['Alpine JS is Amazing', 'It is Truly Awesome!', 'You Have to Try It!'],
                    textIndex: 0,
                    charIndex: 0,
                    pauseEnd: 1000,
                    pauseStart: 20,
                    typeSpeed: 75,
                    direction: 'forward'
                 }" x-init="(() => { 
                 
                    let typingInterval = setInterval(startTyping, $data.typeSpeed);
                 
                 function startTyping(){
                    let current = $data.textArray[ $data.textIndex ];
                    if($data.charIndex > current.length){
                         $data.direction = 'backward';
                         clearInterval(typingInterval);
                         setTimeout(function(){
                            typingInterval = setInterval(startTyping, $data.typeSpeed);
                         }, $data.pauseEnd);
                    }   
                       
                    $data.text = current.substring(0, $data.charIndex);
                    if($data.direction == 'forward'){
                        $data.charIndex += 1;
                     } else {
                        if($data.charIndex == 0){
                            $data.direction = 'forward';
                            clearInterval(typingInterval);
                            setTimeout(function(){
                            
                                $data.textIndex += 1;
                                if($data.textIndex >= $data.textArray.length){
                                    $data.textIndex = 0;
                                }
                            
                                typingInterval = setInterval(startTyping, $data.typeSpeed);
                            }, $data.pauseStart);
                        }
                        $data.charIndex -= 1;
                     }
                 }
             })()">
        <h1 class="text-7xl font-black" x-text="text"></h1>
    </div>
</div>

Here is a codepen example of the code above:

Now, we are typing each character and looping through each line. Finally, we may want to add a little blinking cursor animation to wrap things up.

Adding a Blinking Cursor

Finally, we may want to add a blinking cursor to the end of our component element. We can easily do that by toggling hidden from a block at the end of the element. Here's the code for the final example:

<div class="w-screen h-screen flex items-center justify-center">
    <div class="max-w-7xl mx-auto text-center" x-data="{
                    text: '',
                    textArray : ['Alpine JS is Amazing', 'It is Truly Awesome!', 'You Have to Try It!'],
                    textIndex: 0,
                    charIndex: 0,
                    pauseEnd: 1500,
                    cursorSpeed: 420,
                    pauseStart: 20,
                    typeSpeed: 110,
                    direction: 'forward'
                 }" x-init="(() => { 
                 
                    let typingInterval = setInterval(startTyping, $data.typeSpeed);
                 
                 function startTyping(){
                    let current = $data.textArray[ $data.textIndex ];
                    if($data.charIndex > current.length){
                         $data.direction = 'backward';
                         clearInterval(typingInterval);
                         setTimeout(function(){
                            typingInterval = setInterval(startTyping, $data.typeSpeed);
                         }, $data.pauseEnd);
                    }   
                       
                    $data.text = current.substring(0, $data.charIndex);
                    if($data.direction == 'forward'){
                        $data.charIndex += 1;
                     } else {
                        if($data.charIndex == 0){
                            $data.direction = 'forward';
                            clearInterval(typingInterval);
                            setTimeout(function(){
                            
                                $data.textIndex += 1;
                                if($data.textIndex >= $data.textArray.length){
                                    $data.textIndex = 0;
                                }
                            
                                typingInterval = setInterval(startTyping, $data.typeSpeed);
                            }, $data.pauseStart);
                        }
                        $data.charIndex -= 1;
                     }
                  
                 }
                            
                 setInterval(function(){
                    if($refs.cursor.classList.contains('hidden')){
                        $refs.cursor.classList.remove('hidden');
                    } else {
                        $refs.cursor.classList.add('hidden');
                    }
                 }, $data.cursorSpeed);
             })()">
        <div class="relative h-auto">
            <h1 class="text-7xl font-black" x-text="text"></h1>
            <span class="absolute right-0 top-0 h-full w-5 -mr-8 bg-black" x-ref="cursor"></div>
        </div>
    </div>
</div>

And here is that final example in action.

Conclusion

This was a simple tutorial on creating a typing effect using AlpineJS. There are many other things that we can clean up and values that we can tweak to make it really pop. Feel free to use this code as you see fit 👨‍💻

Using Alpine, we can create a bunch of cool javascript components with very minimal code. Be sure to give me a follow if you wish to learn about future posts I write on AlpineJS.

Happy Coding and Typing 😉