Type Writer

A cool typewriter effect where you input list of sentences or one and it will type it

Installation

Start by installing the required dependencies:

tsx
1npm install gsap @gsap/react

Then, copy the following component code into your project:

components/ui/stacked-cards.tsx
1'use client'
2import React, { useEffect, useRef } from 'react';
3import { gsap } from 'gsap';
4import { TextPlugin } from 'gsap/TextPlugin';
5import { cn } from '@/lib/utils'; 
6
7gsap.registerPlugin(TextPlugin);
8
9
10export interface TypewriterProps {
11  texts: string[];
12  className?: string;
13  textClassName?: string;
14  cursorClassName?: string;
15  cursorChar?: string;
16  typingSpeed?: number;
17  deletingSpeed?: number;
18  delayBetweenTexts?: number;
19  loop?: boolean;
20}
21
22
23export const Typewriter: React.FC<TypewriterProps> = ({
24  texts,
25  className,
26  textClassName,
27  cursorClassName,
28  cursorChar = '|',
29  typingSpeed = 0.05,
30  deletingSpeed = 0.03,
31  delayBetweenTexts = 1.5,
32  loop = true,
33}) => {
34  const textRef = useRef<HTMLSpanElement>(null);
35  const cursorRef = useRef<HTMLSpanElement>(null);
36
37  useEffect(() => {
38    if (!textRef.current || !cursorRef.current || texts.length === 0) return;
39
40    const cursorTween = gsap.to(cursorRef.current, {
41      opacity: 0,
42      ease: 'power2.inOut',
43      duration: 0.7,
44      repeat: -1,
45      yoyo: true,
46    });
47
48    const masterTimeline = gsap.timeline({
49      repeat: loop ? -1 : 0,
50    });
51
52    texts.forEach((text, i) => {
53      const textTimeline = gsap.timeline();
54      
55      textTimeline.to(textRef.current, {
56        duration: text.length * typingSpeed,
57        text: {
58          value: text,
59          newClass: "is-typing" 
60        },
61        ease: 'none',
62      })
63
64      .to(textRef.current, {
65        duration: text.length * deletingSpeed,
66        text: {
67          value: "",
68          newClass: "is-deleting"
69        },
70        ease: 'none',
71        delay: delayBetweenTexts,
72
73        onComplete: () => {
74          if (!loop && i === texts.length - 1) {
75            cursorTween.pause();
76            gsap.to(cursorRef.current, { opacity: 1, duration: 0.2 });
77          }
78        }
79      });
80      
81      masterTimeline.add(textTimeline);
82    });
83    
84    return () => {
85      masterTimeline.kill();
86      cursorTween.kill();
87    };
88
89  }, [texts, loop, typingSpeed, deletingSpeed, delayBetweenTexts]);
90
91  return (
92    <div
93      className={cn(
94        'inline-flex items-center justify-center p-6 sm:p-8 rounded-2xl border border-white/10 bg-black/20 backdrop-blur-lg shadow-2xl shadow-black/30',
95        className
96      )}
97    >
98      <span
99        ref={textRef}
100        className={cn(
101          'text-2xl md:text-4xl lg:text-5xl font-mono font-medium text-white tracking-wider',
102          textClassName
103        )}
104      />
105      <span
106        ref={cursorRef}
107        aria-hidden="true"
108        className={cn(
109          'text-2xl md:text-4xl lg:text-5xl font-mono font-medium text-cyan-300 ml-1',
110          cursorClassName
111        )}
112      >
113     {cursorChar}
114      </span>
115    </div>
116  );
117};
Popular Components