Vue 3 Typewriter Component

Feb 05, 2023 (1748 views)

Create the Typewriter component

Typewriter.vue
<script setup lang="ts">
  // Vue 3.5+ - Reactive Prop Destructure 
  const { 
    data = [],
    start = 1000,
    enter = 60,
    end = 1500,
    leave = 30
  } = defineProps<{
    data: Array<string>;
    start?: number;
    enter?: number;
    end?: number;
    leave?: number;
  }>();

  const state = reactive({ text: '', complete: false, index: 0 });

  addText();

  function addText() {
    if (state.text.length < data[state.index].length && !state.complete) {
      state.text += data[state.index].charAt(state.text.length);
      useTimeoutFn(addText, enter);
    }
    if (state.text.length === data[state.index].length) {
      state.complete = true;
      useTimeoutFn(removeText, end);
    }
  }

  function removeText() {
    if (state.text.length > 0) {
      const t = state.text.split('');
      t.pop();
      state.text = t.join('');
      useTimeoutFn(removeText, leave);
    }
    if (state.text.length === 0 && state.complete) {
      state.complete = false;
      if (state.index === data.length - 1) {
        state.index = 0;
      } else {
        state.index++;
      }

      useTimeoutFn(addText, start);
    }
  }
</script>

<template>
  <p
    class="flex items-center text-lg h-5 animate-blink 
    border-r-2 border-transparent w-fit font-semibold dark:text-gray-200"
  >
    {{ state.text }}
  </p>
</template>

Add blink animation to tailwind stylesheet

tailwind.css
/* tailwind 4+ */
@theme {
  --animate-blink: blink 1s infinite;

  @keyframes blink {
    0% {
      border-color: transparent;
    }

    45% {
      border-color: transparent;
    }

    50% {
      border-color: var(--color-gray-400);
    }

    100% {
      border-color: var(--color-gray-400);
    }
  }
}

Usage

component.vue
<script>
const data = [
  'Typewriter Component',
  'Built with Vue 3, TypeScript, & Tailwind'
];
</script>

<template>
  <Typewriter :data="data" />
</template>

Result