A vintage typewriter typing by itself in a cozy study with a red maine coon cat watching from an armchair

There is something deeply satisfying about watching text appear on screen one character at a time. It draws the eye, holds attention, and turns a static heading into a small performance. Typed.js is the library that has been delivering that performance for over a decade. It simulates a human typing and deleting strings of text, complete with configurable speed, pauses, and a blinking cursor. You hand it an array of strings and a target element, and it handles the rest.

typed.js weighs in at roughly 3.3 KB gzipped with zero dependencies. It works in plain HTML pages, React apps, Vue projects, and anything else that has a DOM. Companies like Slack, GitHub, and Envato use it in production. With version 3.0.0 released in January 2026, it remains the most downloaded typing animation library on npm by a wide margin, pulling in around 130,000 weekly downloads.

What the Ghost Can Do

Typed.js is not a one-trick library. Beyond the basic type-and-delete loop, it offers a surprisingly rich feature set:

  • Smart backspace that only deletes characters differing from the next string, creating a natural editing feel
  • Inline pausing with a simple ^1000 syntax to pause mid-sentence for dramatic effect
  • Fade out mode that replaces backspacing with a smooth opacity transition
  • HTML content support so you can type bold text, links, and other markup
  • SEO-friendly mode that reads strings from the DOM instead of JavaScript, keeping content visible to search crawlers
  • Bulk typing for simulating terminal output with newlines and delayed responses
  • A comprehensive callback system covering every phase of the typing lifecycle

Getting the Ghost on Your Machine

Install with your package manager of choice:

// npm
npm install typed.js

// yarn
yarn add typed.js

Your First Haunting

Hello, Typing World

The simplest use case is typing a few strings into an element. Create a target element in your HTML and point Typed.js at it:

import Typed from 'typed.js';

const typed = new Typed('#hero-text', {
  strings: [
    'We build products.',
    'We build experiences.',
    'We build the future.',
  ],
  typeSpeed: 50,
  backSpeed: 30,
  backDelay: 1500,
  loop: true,
});

Each string types out at 50 milliseconds per character, waits 1.5 seconds, then backspaces at 30 milliseconds per character before moving to the next one. The loop: true option keeps the cycle going indefinitely. That is all it takes to turn a static heading into an eye-catching animation.

The Dramatic Pause

Sometimes you want the typing to hesitate, as if the invisible typist is thinking. Typed.js supports inline pauses with the caret syntax:

const typed = new Typed('#terminal', {
  strings: [
    'Initializing system...^1500 Done.',
    'Loading modules...^800 3 modules loaded.^500 Ready.',
  ],
  typeSpeed: 40,
  showCursor: true,
  cursorChar: '_',
});

The ^1500 pauses typing for 1.5 seconds before continuing with "Done." This creates a convincing simulation of a process running in real time. Combined with a custom cursor character like _, it starts to feel like a real terminal.

Typing into Attributes

Typed.js can type into element attributes rather than innerHTML. This is particularly useful for animated placeholder text in search fields:

const typed = new Typed('#search-input', {
  strings: [
    'Search for recipes...',
    'Search for restaurants...',
    'Search for cooking tips...',
  ],
  typeSpeed: 60,
  backSpeed: 40,
  attr: 'placeholder',
  loop: true,
  showCursor: false,
});

The attr option tells Typed.js to update the element's placeholder attribute instead of its text content. The cursor is hidden here because the input field already provides its own.

Advanced Conjurations

Terminal Simulation

For developers building portfolio sites or documentation pages, Typed.js can simulate a full terminal session with multi-line output:

const typed = new Typed('#terminal-output', {
  strings: [
    'npm install typed.js^500\n`added 1 package in 0.8s`',
    'node index.js^800\n`Server running on port 3000`^500\n`Accepting connections...`',
  ],
  typeSpeed: 30,
  backSpeed: 0,
  backDelay: 2000,
  contentType: 'html',
  showCursor: true,
  cursorChar: '|',
});

Text wrapped in backticks is treated as output that appears instantly rather than being typed character by character. This two-speed approach, where commands type out slowly but their output appears at once, sells the terminal illusion.

React Integration

Typed.js works cleanly with React using the useRef and useEffect hooks. No wrapper library required:

import { useEffect, useRef } from 'react';
import Typed from 'typed.js';

interface HeroTypingProps {
  strings: string[];
  speed?: number;
}

function HeroTyping({ strings, speed = 50 }: HeroTypingProps) {
  const el = useRef<HTMLSpanElement>(null);

  useEffect(() => {
    if (!el.current) return;

    const typed = new Typed(el.current, {
      strings,
      typeSpeed: speed,
      backSpeed: 30,
      backDelay: 1200,
      loop: true,
      smartBackspace: true,
    });

    return () => {
      typed.destroy();
    };
  }, [strings, speed]);

  return <span ref={el} />;
}

export default HeroTyping;

The critical detail is calling typed.destroy() in the cleanup function. This removes event listeners and stops the animation, preventing memory leaks when the component unmounts. The smartBackspace option is enabled here so that when consecutive strings share a common prefix, only the differing characters get deleted and retyped.

Lifecycle Hooks and Fine Control

Typed.js exposes callbacks for every stage of its animation cycle, letting you synchronize other UI changes with the typing:

const typed = new Typed('#animated-text', {
  strings: [
    'Building user interfaces',
    'Crafting developer tools',
    'Shipping great software',
  ],
  typeSpeed: 45,
  backSpeed: 25,
  loop: true,
  preStringTyped: (arrayPos: number, self: Typed) => {
    const colors = ['#3b82f6', '#10b981', '#f59e0b'];
    const container = document.getElementById('animated-text');
    if (container) {
      container.style.color = colors[arrayPos];
    }
  },
  onStringTyped: (arrayPos: number, self: Typed) => {
    console.log(`Finished typing string ${arrayPos}`);
  },
  onComplete: (self: Typed) => {
    console.log('All strings have been typed');
  },
});

The preStringTyped callback fires before each new string starts, receiving the index of the upcoming string. Here it changes the text color to match each phrase. The onStringTyped callback fires after each string finishes typing, and onComplete fires when the entire sequence ends (only relevant when loop is false). Other callbacks like onTypingPaused, onTypingResumed, and onLastStringBackspaced give you granular control over the animation timeline.

SEO-Friendly Strings

If search engine visibility matters, you can provide strings directly in the HTML instead of in JavaScript. This way crawlers and users with JavaScript disabled still see your content:

// HTML:
// <div id="typed-strings">
//   <p>We build products.</p>
//   <p>We build experiences.</p>
// </div>
// <span id="typed-target"></span>

const typed = new Typed('#typed-target', {
  stringsElement: '#typed-strings',
  typeSpeed: 50,
  loop: true,
});

Typed.js reads the text from the #typed-strings element and hides it, then types the content into #typed-target. The original text remains in the DOM for search engines to index.

When the Typing Stops

Typed.js has earned its place as the default choice for typing animations on the web. It is tiny, dependency-free, battle-tested across thousands of production sites, and flexible enough to handle everything from a simple hero heading to a multi-line terminal simulation. The React integration is clean, the callback system is thorough, and the smart backspace feature adds a subtle polish that most alternatives lack. If your landing page needs a touch of kinetic personality, typed.js is the ghost you want haunting your keyboard.