npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

typemorphjs

v1.0.4

Published

Typing animation library that supports Markdown, HTML, looping, backspacing, auto scrolling and more. Perfect for things like LLM outputs, documentations or any typing effect in a DOM environment.

Readme

TypeMorph

Tests Coverage npm version

TypeMorph is a lightweight JavaScript library for creating smooth, realistic typing effects. Supports features such as looping, backspacing, HTML, markdown, autoscrolling, and animated cursor. Perfect for hero sections, documentation intros, LLM chat animation, or anywhere you want animated text.

🚀 Features

  • Typing engine: character typing with customizable speed and chunking
  • Looping modes: clear or backspace styles
  • Backspace control: define speed, delay, and behavior
  • Markdown support: render markdown with Marked
  • HTML support: HTML is sanitized with DOMPurify
  • Configurable cursor: built in and customizable blink animation
  • Async API: control typing flow with type(), loop(), stop(), and destroy()
  • Framework agnostic: works in plain JS, React, Vue, or anywhere with a DOM

📦 Installation

Using npm

npm install typemorphjs

Then import it:

import TypeMorph from "typemorphjs";

Using a CDN

<script src="https://cdn.jsdelivr.net/npm/typemorphjs/dist/typemorph.umd.min.js"></script>
<script>
  const typer = new TypeMorph({ parent: document.getElementById("target") });
  typer.type("Hello, world!");
</script>

Core version

The core version of the library has 0 dependencies and is smaller in size, if you either provide your own HTML/MD sanitizer/parser or simply disable these features, you can use the core only for a lighter version of the library:

<script src="https://cdn.jsdelivr.net/npm/typemorphjs/dist/typemorph.core.umd.min.js"></script>

⚡ Quick Start

Type

<div id="target"></div>

<script>
  const typer = new TypeMorph({
    parent: document.getElementById("target"),
    speed: 50,
  });

  typer.type("Hello from TypeMorph!");
</script>

Loop

const typer = new TypeMorph({
  parent: document.getElementById("target"),
  speed: 50,
  loopType: "backspace",
  loopCount: 5,
});

typer.loop("This text will loop and backspace 5 times!");

HTML

const typer = new TypeMorph({
  parent: document.getElementById("target"),
  parseHtml: true,
});

typer.type('<h1>Hello <span style="color: red">there!</span></h1>');

Makrdown

const typer = new TypeMorph({
  parent: document.getElementById("target"),
  parseMarkdown: true,
});

typer.type("**This is real fun**");

Execution Flow

const typer = new TypeMorph({
  parent: document.getElementById("target"),
  loopCount: 1,
  loopType: "backspace",
  loopFinalBehavior: "remove",
  loopEndDelay: 500,
});

await typer.loop("**This is real fun**");
await typer.loop("**I want more**", { loopFinalBehavior: "keep" });

The blinking cursor is automatically injected as a <style> element. If you want to customize the cursor style, you can use the below CSS class:

.typemorph-cursor {
  color: red;
  animation-duration: 0.8s;
}

⚙️ Configuration Options

| Option | Type | Default | Description | | -------------------------- | ------------------------ | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | | text | string \| null | null | Optional initial text for this instance. | | parent | HTMLElement \| string | null | The target element or its id where the text will appear. | | speed | number | 50 | Delay in milliseconds per character (or chunk). | | chunkSize | number | 1 | Number of characters typed in one iteration. | | loopCount | number | Infinity | Number of loops before stopping. | | loopType | "clear" \| "backspace" | "backspace" | Defines how text is removed between loops, cleared instantly or backspaced. | | loopFinalBehavior | "keep" \| "remove" | "keep" | Defines behavior at the final loop. "keep" just keeps the text at the end, "remove" deletes it (respecting "loopType"). | | loopStartDelay | number | 300 | Delay before restarting the typing after clearing/backspacing. | | loopEndDelay | number | 800 | Delay after typing finishes, before starting backspacing/clearing. | | backspaceSpeed | number | 50 | Delay in milliseconds per character when backspacing. | | showCursor | boolean | true | Shows a blinking cursor (\|) at the end of the text. | | cursorChar | string | \| | The character used for the cursor. | | parseMarkdown | boolean | false | If true, parses markdown syntax into HTML (implies parseHtml = true). | | markdownInline | boolean | false | Parses markdown inline, avoiding unwanted block wrappers for short text. | | parseHtml | boolean | true | Whether to interpret HTML in the text. If you are using parseMarkdown with this, it's recommended you set markdownInline to true to avoid layout issues. | | markdownParse | function \| null | null | Custom markdown parser => (text, inline) => html. | | hideCursorOnFinishTyping | boolean | true | Automatically hides the cursor when typing completes (if not looping). | | autoScroll | boolean | true | Automatically scrolls the parent element to end while typing, Unless user scrolls manually | | scrollContainer | HTMLElement \| string | null | Custom scroll container. If not provided, the current typing parent is the target for autoscroll | | scrollInterval | number | 1 | Number of chunks typed before auto-scroll triggers. | | smoothScroll | boolean | false | Whether to use smooth scrolling. This is not recommended in most cases because it's not reliable for text with line breaks and rapid height changes. | | clearBeforeTyping | boolean | true | If true, clears the parent’s text before typing new text. | | htmlSanitize | function \| null | null | Custom HTML sanitizer => (html) => safeHtml. | | onStop | function(instance) | () => {} | Called when a typing operation is stopped manually via .stop(). | | onFinish | function(instance) | () => {} | Called when typing completes naturally (no loop or final loop iteration). | | onDestroy | function(instance) | () => {} | Called when the instance is destroyed and all resources are cleaned up. |


🧩 API Methods

| Method | Returns | Description | | ------------------------------------------------------------- | --------------- | ------------------------------------------------------------------------- | | type(text: string, parent?: HTMLElement, options = {}) | Promise<void> | Types the provided text into the target element once. | | loop(text?: string, parent?: HTMLElement, options = {}) | Promise<void> | Starts looping typing animation using the configured loopType. | | stop() | Promise<void> | Gracefully stops any ongoing typing or looping operation. | | destroy() | void | Stops all operations, removes timers, event listeners, and the cursor. | | isTyping() | boolean | Returns whether the instance is currently typing, looping or backspacing. | | getCurrentLoop() | number | Returns the current loop iteration index. Useful for monitoring progress. |


🔔 Event Callbacks

Each event callback receives the TypeMorph instance as its first argument

| Callback | Trigger | Example | | --------------------- | ------------------------------------------------------------ | ------------------------------------------------------ | | onStop(instance) | When typing or looping is stopped manually via .stop() | onStop: (t) => console.log("Stopped:", t.isTyping()) | | onFinish(instance) | When typing completes naturally (no further loops) | onFinish: (t) => console.log("Done typing!") | | onDestroy(instance) | When .destroy() is called and instance cleanup is executed | onDestroy: () => console.log("TypeMorph destroyed") |


🧱 Example Configuration

const typer = new TypeMorph({
  parent: document.querySelector("#target"),
  speed: 60,
  backspaceSpeed: 40,
  chunkSize: 3,
  loopCount: 3,
  loopType: "backspace",
  parseMarkdown: true,
  autoScroll: false,
  onFinish: (t) => console.log("Typing finished"),
});

typer.loop("**Bold** _italic_ text with `code`");

Custom per-run Options

Functions that take options will override initial configs (that can be overriden) for this current run only. For example, you can do something like this:

const typer = new TypeMorph({
  parent: document.querySelector("#target"),
  speed: 300,
});

await typer.type("Slow text");
await typer.type("Fast text", { speed: 30 });

🧰 Development

Install deps

npm install

Run tests

npm test

Run tests + coverage data

npm run test:coverage

Build dist/

npm run build

⭐ Show some love

If you find TypeMorph useful, consider starring the repo, it helps others discover it!