untyper
v2.0.0
Published
a simple typewriter for browser
Maintainers
Readme
^^_^^ untyper
Looking forward to your feedback

Live demo
A typewriter utility for the browser. It builds a queue of actions so you can chain type, pause, delete, move, add, image, and then run them with go().
🚀 Features
- Chained typing actions powered by the Web Animations API cursor
- Customizable typing speed, start delay, and cursor animation
- Supports plain text typing as well as inserting HTML elements and images
- Optional per-element animations when inserting DOM nodes
- Works with any DOM element
📦 Install
npm install untyperUsage
import { UnTyper } from 'untyper'
const target = document.querySelector('#text')
const typer = new UnTyper(target, {
speed: 90,
startDelay: 300,
cursorAnimation: {
kind: 'combined',
duration: 900,
},
})
await typer
.type('Hi there!')
.pause(400)
.delete(6)
.type('untyper!')
.go()API
new UnTyper(element, options)
| Option | Type | Default | Description |
| --- | --- | --- | --- |
| speed | number | 120 | Base delay between characters (ms). |
| startDelay | number | 0 | Delay before the queue starts (ms). |
| animationspancontent | string | | | Cursor character. |
| animate.cancel | boolean | false | Hide cursor after the queue completes. |
| cursorAnimation | CursorAnimationOptions | — | Cursor animation customization. |
| plugins | UnTyperPluginUse[] | [] | Plugins installed when the instance is created. |
type(text, options)
Adds a plain text typing action. HTML is not supported here.
text:stringoptions.delay: optional delay (ms) after typing this text
Returns this.
pause(ms)
Adds a pause action.
ms:number
Returns this.
delete(charCount, options)
Deletes characters from the current cursor position.
charCount:number(must be greater than 0)options.delay: optional delay (ms) after deleting
Returns this.
move(movementArg, options)
Moves the cursor.
movementArg:number | null- Provide a negative number to move left by that many characters.
- Use
nullwithoptions.to = 'start' | 'end'to jump to the start or end.
Returns this.
add(html, options)
Parses HTML and inserts elements with typing animation for text nodes. You can pass options.animation to animate inserted elements.
html:stringoptions.delay: optional delay (ms) after addingoptions.animation: optionalElementAnimationto apply to added elements
Returns this.
image(src, options)
Inserts an img element with optional attributes. You can also pass options.animation to animate the image after insertion.
src:stringoptions.alt:stringoptions.className:stringoptions.width:numberoptions.height:numberoptions.attrs:Record<string, string>for additional attributesoptions.delay: optional delay (ms) after insertingoptions.animation: optionalElementAnimationto apply to the image
Returns this.
use(plugin, options)
Installs a plugin after the instance is created. A plugin can be a function or an object with name and install(ctx, options).
Returns this.
registerAction(name, handler)
Registers a chainable custom action.
Returns this.
action(name, ...args)
Runs a custom action registered by a plugin or by registerAction.
Returns this.
go()
Runs the queued actions.
Returns Promise<void>.
Animation support
You can apply custom animations to inserted DOM elements using options.animation on add or image.
await typer
.add('<strong>Spotlight</strong>', {
animation: {
keyframes: [
{ opacity: 0, transform: 'translateY(4px)' },
{ opacity: 1, transform: 'translateY(0)' },
],
options: { duration: 300, easing: 'ease-out' },
},
})
.image('/logo.png', {
alt: 'Brand logo',
animation: {
keyframes: [
{ transform: 'scale(0.9)', opacity: 0 },
{ transform: 'scale(1)', opacity: 1 },
],
options: { duration: 250 },
},
})
.go()Plugin API
Plugins follow the common frontend plugin shape: expose a stable install(ctx, options) entrypoint, keep setup side effects inside install, and register extension points through the provided context instead of reaching into private fields.
import type { UnTyperPlugin } from 'untyper'
const emojiPlugin: UnTyperPlugin<{ suffix?: string }> = {
name: 'emoji',
install(ctx, options) {
ctx.registerAction('emoji', (api, text: string) => {
api.insertText(`${text}${options?.suffix ?? ''}`)
})
ctx.hook('afterRun', ({ root }) => {
root.setAttribute('data-untyper-complete', 'true')
})
},
}
await new UnTyper(target, {
speed: 80,
plugins: [[emojiPlugin, { suffix: '!' }]],
})
.type('Status: ')
.action('emoji', 'ready')
.go()Plugin context:
| API | Description |
| --- | --- |
| ctx.registerAction(name, handler) | Adds a named custom action that can be called with action(name, ...args). |
| ctx.enqueue(steps, options) | Adds raw queue steps for advanced plugins. Queue func handlers may be async. |
| ctx.insertText(text, options) | Reuses the built-in text typing behavior. |
| ctx.insertElement(element, options) | Reuses the built-in DOM insertion behavior. |
| ctx.hook(name, handler) | Registers lifecycle hooks: beforeRun, afterRun, beforeStep, afterStep, onError. |
| ctx.root, ctx.cursor, ctx.options | Read-only access to the target element, cursor, and resolved options. |
