Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

173
Views
How to setup multiline typing effect for react with delay between lines

I have created a class-based component in React that starts a multiline typing effect on load.

It is working alright, but the issue is that both the lines start typing simultaneously, which makes sense given the nature of how setTimout is executed.

So I'm looking for any ideas/suggestions on how to make it wait for the first line to finish being typed before going on to the next one?

import React from 'react'
import TerminalCursor from 'icons/TerminalCursor'
import TerminalPrompt from 'icons/TerminalPrompt'
import { v4 as uuidv4 } from 'uuid'

type MyProps = {}
type MyState = {
  introArray: string[]
}
class Terminal extends React.Component<MyProps, MyState> {
  state: MyState = {
    introArray: [],
  }

  componentDidMount() {
    // Data to import from sanity
    const starterArray = [
      'The very first line that needs to finish typing before going to the next line',
      'The second line that needs to start being typed after the first line',
    ]

    const createTypingEffect = (text: string, index: number) => {
      for (let i = 0; i < text.length; i++) {
        setTimeout(() => {
          let arrayCopy = this.state.introArray.slice()
          this.setState((state) => ({
            introArray: [...state.introArray.slice(0, index), arrayCopy[index] + text[i], ...state.introArray.slice(index + 1)],
          }))
        }, 100 * i)
      }
    }
    starterArray.forEach((starterText, starterIndex) => {
      // Setting empty string for each line in starterArray so we dont get undefined as first character
      this.setState((state) => ({
        introArray: [...state.introArray, ''],
      }))

      // Need to wait for first line to finish typing before starting the second line
      createTypingEffect(starterText, starterIndex)
    })
  }
  render() {
    return (
      <div className="w-1/2 h-1/2 p-5 flex items-start justify-start bg-clip-padding bg-slate-900 backdrop-filter backdrop-blur-xl bg-opacity-60 border border-gray-900 rounded">
        <div className="flex flex-col">
          {this.state.introArray.map((introLine) => (
            <div className="flex items-center" key={uuidv4()}>
              <TerminalPrompt />
              <p className="text-white">{introLine}</p>
            </div>
          ))}
          {/* Actual prompt starts here */}
          <div className="flex">
            <TerminalPrompt />
            <TerminalCursor />
          </div>
        </div>
      </div>
    )
  }
}

export default Terminal

Here is a sandbox demo (not sure why some characters are being repeated in the sandbox, don't have this issue on local...)

https://codesandbox.io/s/recursing-star-iohjw9?file=/src/Terminal.tsx

about 4 years ago · Juan Pablo Isaza
1 answers
Answer question

0

You need to use an asynchronous approach for this kind of tasks ( I did not check your typing effect logic, I just made the whole process asynchronous ):

import React from "react";

type MyProps = {};
type MyState = {
  introArray: string[];
};
class Terminal extends React.Component<MyProps, MyState> {
  state: MyState = {
    introArray: []
  };

  componentDidMount() {
    // Data to import from sanity
    const starterArray = [
      "The very first line that needs to finish typing before going to the next line",
      "The second line that needs to start being typed after the first line"
    ];

    const createTypingEffect = async (text: string, index: number) => {
      return Promise.all(
        text.split("").map(
          (c, i) =>
            new Promise((res) => {
              setTimeout(() => {
                let arrayCopy = this.state.introArray.slice();
                this.setState((state) => ({
                  introArray: [
                    ...state.introArray.slice(0, index),
                    arrayCopy[index] + c,
                    ...state.introArray.slice(index + 1)
                  ]
                }));
                res(null);
              }, 100 * i);
            })
        )
      );
    };

    const cycle = async () => {
      let i = 0;
      for (const starterText of starterArray) {
        // Setting empty string for each line in starterArray so we dont get undefined as first character
        this.setState((state) => ({
          introArray: [...state.introArray, ""]
        }));

        await createTypingEffect(starterText, i);
        i++;
      }
    };

    cycle();
  }
  render() {
    console.log(this.state.introArray);
    return (
      <div className="w-1/2 h-1/2 p-5 flex items-start justify-start bg-clip-padding bg-slate-900 backdrop-filter backdrop-blur-xl bg-opacity-60 border border-gray-900 rounded">
        <div className="flex flex-col">
          {this.state.introArray.map((introLine, index) => (
            <div className="flex items-center" key={index}>
              <p className="text-white">{introLine}</p>
            </div>
          ))}
        </div>
      </div>
    );
  }
}

export default Terminal;

You can use several approaches to handle the async logic, I used a Promise.all approach here, giving an incremental timer to the timeout, just to follow what you were doing. An alternative solution is to use a simple for...of loop and await for a promisified timeout inside. That way you don't need to give an incremental time to the timeout, since they will be executed relatively synchronously.

The working demo is HERE ( Working snippet can't be added here since SO has no TS support)

about 4 years ago · Juan Pablo Isaza Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!