Migration Guide
Version 6 is a full rewrite
Version 6.0.0 is not backward-compatible with any prior release. The API surface, concepts, and package structure are entirely new. There is no incremental upgrade path; adopt v6 as a fresh integration.
What changed
Architecture
Versions before 6 mutated a DOM element's innerHTML directly in a recursive setTimeout loop. Version 6 introduces a structured pipeline:
- A declarative timeline builder replaces imperative method calls.
- A compiler converts commands into timestamped events.
- A reducer applies events to an immutable state object.
- A renderer interface decouples state from output.
The result is that the same animation can target a DOM element, a string buffer, a canvas, or a terminal without changing any timeline code.
Package name
The package name is unchanged: eo-typewriterjs. Update to ^6.0.0 in your package.json.
pnpm add eo-typewriterjs@^6.0.0Entry point
Version 6 is an ES module with named exports. There is no default export.
// v6
import { createTypewriter, domRenderer, stringRenderer } from "eo-typewriterjs";Creating a typewriter instance
In v5, Typewriter was a class constructor that accepted a CSS selector string and an options object:
// v5
var tw = new Typewriter("#target");
var tw = new Typewriter("#target", { tick: 100, delay: 500 });In v6, use the createTypewriter factory and pass a renderer that holds the output target:
// v6
import { createTypewriter, domRenderer } from "eo-typewriterjs";
const el = document.getElementById("output")!;
const tw = createTypewriter({ renderer: domRenderer(el) });Defining and starting animations
In v5, each method call started executing immediately (after its delay option) and returned a Promise. Sequencing required .then() chaining or await:
// v5: promise chaining
var tw = new Typewriter("#target", { tick: 100 });
tw.type("Hello").then(() => tw.delete(5)).then(() => tw.type("World!"));
// v5: async/await
await tw.type("Hello");
await tw.delete(5);
await tw.type("World!");In v6, all commands are registered on tw.timeline first and then executed together when you call tw.play():
// v6
tw.timeline
.type("Hello", { by: "char", interval: 100 })
.delete(-5, { by: "char", interval: 100 })
.type("World!", { by: "char", interval: 100 });
await tw.play();Typing text
// v5
await tw.type("Hello, World!");
// v5 with options
await tw.type("Hello", { tick: 80, delay: 200 });// v6
tw.timeline.type("Hello, World!");
// v6 with options
tw.timeline.type("Hello", { by: "char", interval: 80 });The v5 tick option (ms per character) maps directly to v6's interval option.
Deleting text
In v5, .delete(chars) always deleted backward (toward the start) one character at a time:
// v5: delete 5 characters backward
await tw.delete(5);
// v5: delete 1 character (default)
await tw.delete();In v5, .clear() removed all text instantly (without a character-by-character animation):
// v5: clear all text instantly
await tw.clear();// v6: delete 5 characters to the left of the cursor
tw.timeline.delete(-5);
// v6: delete 5 characters to the right
tw.timeline.delete(5);
// v6: delete entire document (animated, one step)
tw.timeline.delete("whole");Note the sign convention change: v6 uses negative numbers for leftward deletion (backspace direction) and positive for rightward (forward delete direction).
Stopping and resuming
In v5, .stop() paused the current operation and .resume() restarted it from where it stopped:
// v5
await tw.type("Long text...");
await tw.stop({ delay: 1000 });
await tw.resume({ delay: 500 });In v6, the equivalents are .pause() and .play():
// v6
tw.timeline.type("Long text...", { by: "char", interval: 60 });
await tw.play();
tw.pause(); // pause at current position
await tw.play(); // resume from where it stoppedMoving the cursor
In v5, .move(index) took an absolute zero-based index and was synchronous:
// v5: move cursor to position 3 (absolute index)
tw.move(3);In v6, .move() takes a relative offset or a string boundary and is a timeline command:
// v6: move left 3 units
tw.timeline.move(-3);
// v6: move to the start of the document
tw.timeline.move("start");
// v6: move to the end of the document
tw.timeline.move("end");Pausing between steps
In v5, delays were per-method options (delay field):
// v5: start typing after 500 ms
await tw.type("Hello", { delay: 500 });In v6, use .wait() to insert an explicit pause between commands:
// v6: pause 500 ms before typing
tw.timeline
.wait(500)
.type("Hello", { by: "char", interval: 80 });Running code mid-animation
v5 had no built-in way to interleave arbitrary logic inside an animation sequence. You would chain promises externally:
// v5: run code between two operations
await tw.type("Checking...");
await doSomething();
await tw.type("Done!");In v6, use .call() to schedule a callback at a specific point in the timeline:
// v6
tw.timeline
.type("Checking...", { by: "char", interval: 60 })
.call(async () => {
await doSomething();
})
.type("Done!", { by: "char", interval: 60 });
await tw.play();Speed and timing options
In v5, tick controlled the ms-per-character speed in the constructor options and could be overridden per-method:
// v5
var tw = new Typewriter("#target", { tick: 200 });
await tw.type("Slow text");
await tw.type("Fast text", { tick: 50 });In v6, use interval per command or setRate() to scale all timings globally:
// v6: per-command interval
tw.timeline.type("Slow text", { interval: 200 });
tw.timeline.type("Fast text", { interval: 50 });
// v6: scale all timings with a multiplier
tw.setRate(2); // double speed
tw.setRate(1); // restore normal speedCursor configuration
In v5, cursor options were in the constructor config. The cursor type was a CSS class name suffix ("stick" → eo-typewriter__cursor--stick, "underscore" → eo-typewriter__cursor--underscore):
// v5
var tw = new Typewriter("#target", {
cursor: {
type: "stick",
blink: true,
index: 0
}
});In v6, cursor configuration uses ECursorKind constants:
// v6
import { createTypewriter, domRenderer, ECursorKind } from "eo-typewriterjs";
const tw = createTypewriter({
renderer: domRenderer(el),
cursor: {
kind: ECursorKind.PIPE, // replaces cursor.type = "stick"
animation: "blink", // replaces cursor.blink = true
},
});Sound / audio
In v5, audio was configured in the constructor:
// v5
var tw = new Typewriter("#target", {
sound: {
enabled: true,
volume: 0.5
}
});In v6, the audio option has more control, including per-command overrides and playback strategies:
// v6
import { createTypewriter, domRenderer, EAudioStrategy } from "eo-typewriterjs";
const tw = createTypewriter({
renderer: domRenderer(el),
audio: {
enabled: true,
volume: 0.5,
},
});Looping
v5 had no built-in loop option. You would chain recursive promise calls manually. v6 is the same, loop control is explicit:
// v6 looping
await tw.play();
while (true) {
await tw.replay();
}HTML output
v5 rendered its own innerHTML structure using <span> elements per character. Typed text was plain text; there was no rich-text style system.
v6 also works with plain text and provides a separate .style() API for adding CSS classes or inline styles to ranges:
// v6: apply a CSS class to a range of text
tw.timeline
.type("Important")
.style("bold", { from: 0, to: 9 });.bold { font-weight: bold; }Quick migration checklist
- [ ] Replace
new Typewriter(selector, opts)withcreateTypewriter({ renderer: domRenderer(el) }). - [ ] Move all method calls to
tw.timeline.*before callingtw.play(). - [ ] Replace
await tw.type("text")withtw.timeline.type("text")+await tw.play(). - [ ] Replace
tw.delete(n)withtw.timeline.delete(-n)(add the negative sign). - [ ] Replace
tw.clear()withtw.timeline.delete("whole"). - [ ] Replace
{ tick: n }with{ interval: n }on each command. - [ ] Replace
{ delay: n }before a command with.wait(n)on the timeline. - [ ] Replace
tw.stop()/tw.resume()withtw.pause()/tw.play(). - [ ] Replace
tw.move(absoluteIndex)withtw.timeline.move(relativeOffset)ortw.timeline.move("start"/"end"). - [ ] Replace
cursor.type = "stick"withcursor: { kind: ECursorKind.PIPE }. - [ ] Replace
cursor.blink = true/falsewithcursor: { animation: "blink"/"none" }. - [ ] Replace
sound.enabled/volumewithaudio: { enabled, volume }. - [ ] Add interleaved logic using
tw.timeline.call(fn)instead of promise chains.
Staying on v5
If you cannot migrate immediately, keep the specific version pinned:
pnpm add eo-typewriterjs@5v5 is no longer maintained. Security fixes and new features are only available on v6.