webdevpuneet.com

Typewriter Effect — Free HTML CSS JS Snippet

Typewriter Effect · Animations · Plain HTML, CSS & JS · Live preview

CategoryAnimations

Copy & paste

Source Code

<div class="scene">
  <p class="sub">Hi, I'm a developer who</p>
  <h1>
    builds
    <span class="typed-wrap">
      <span id="typed"></span><span class="cursor">|</span>
    </span>
  </h1>
</div>

Extend with AI

Take This Snippet Further With AI

The AI prompt for this one is for Patreon supporters ($3 a month, 7 days free), along with a site with no ads. Supporters enter the passcode once and it is remembered in this browser.

· Become a supporter

What's included

Features

words array cycles infinitely via wi = (wi + 1) % words.length
tick() state machine: deleting boolean switches between type and erase modes
80ms per typed character, 50ms per erased character — faster erase than type
1800ms pause at full word completion before starting erase
CSS blink cursor: animation: blink 0.7s step-end infinite — hard on/off flash
clamp() font-size scales headline without media queries
Zero dependencies — setTimeout recursive loop, no library needed
Export as HTML file, React JSX, or React + Tailwind CSS
Mobile (375px), Tablet (768px), Desktop device preview buttons
Live split-pane editor — preview updates as you type

About this UI Snippet

Typewriter Effect — words Array, tick() State Machine & CSS Blink Cursor

Screenshot of the Typewriter Effect snippet rendered live

The typewriter effect types words character by character, pauses at completion, erases character by character, then moves to the next word. Combined with a CSS blinking cursor, it creates the impression of a live terminal or a human typing. Used on hero sections (see the word-flip hero) to cycle through product values, personas, or use cases without static text — for a decode-style reveal instead, see the text scramble.

The tick() state machine

tick() is a recursive function driven by setTimeout. It has two states controlled by the deleting boolean. In the typing state (!deleting): el.textContent = word.slice(0, ++ci) adds one character per tick at 80ms. When ci === word.length the word is complete — deleting = true and a 1800ms pause happens before the next tick. In the erasing state (deleting): el.textContent = word.slice(0, --ci) removes one character per tick at 50ms (faster erase than type). When ci === 0, wi = (wi + 1) % words.length advances to the next word and deleting = false.

The words array

const words = ['fast UIs', 'clean code', 'great DX', ...] — replace with your own phrases. The array loops infinitely via modulo. Shorter words erase faster; longer words have more visible typing time.

The CSS blink cursor

The .cursor element is a | character with animation: blink 0.7s step-end infinite. step-end` makes the cursor snap on/off instantly rather than fading — matching the hard blink of a real terminal cursor.

The character-by-character typing loop

type() appends one character per call using setInterval at a speed of typically 80-100ms per character. It reads from the current word in the words array at the current charIndex position. When charIndex equals the word length, the typing phase ends and a pause begins before erasing starts. erase() decrements charIndex, removing the last character each interval at a faster speed (40-50ms). When charIndex reaches 0, the word index advances and the next word begins typing.

The blinking cursor

A ::after pseudo-element on .typewriter-text with content: '|' or a separate .cursor element blinks via a CSS keyframe: @keyframes blink { 0%,100%{opacity:1} 50%{opacity:0} }. The cursor animates continuously but should be paused during typing: animation-play-state: paused on the .typing class. This matches how real terminal cursors behave — blinking when waiting, solid when typing.

Infinite loop and word cycling

The words array loops: when the last word finishes typing and erasing, the word index resets to 0. The loop repeats indefinitely. To pause at specific words, add a custom pauseTime property per word and use setTimeout instead of the interval for that word's display duration.

Step by step

How to Use

  1. 1
    Watch the effect in the previewThe typewriter cycles through words, pausing 1.8s at each full word before erasing and typing the next.
  2. 2
    Update the words arrayIn the JS panel, update the words array with your own phrases — product values, user roles, or features.
  3. 3
    Change the fixed prefix textIn the HTML panel, update the static "Build" text that precedes the animated word.
  4. 4
    Change typing and erasing speedIn the JS panel, update 80 (ms per typed character) and 50 (ms per erased character) in the setTimeout calls.
  5. 5
    Change the pause durationUpdate 1800 in the setTimeout that fires when the full word is typed.
  6. 6
    Export in your formatClick "HTML" for a standalone file, "JSX" for a React component, or "Tailwind" for a React + Tailwind version.

Real-world uses

Common Use Cases

Hero headline dynamic keyword cycling
Cycle through product values, user personas, or features in a hero headline: "Build [fast UIs / clean APIs / great DX]". Keeps the headline fresh without needing separate pages.
Portfolio tagline animation
Show your skills cycling in a typewriter: "I build [React apps / REST APIs / pixel-perfect UIs]". More engaging than a static tagline.
Learn setTimeout state machines
The typewriter uses a recursive tick() function with two states. Edit the typing and erasing speeds in the JS panel to understand how the state machine controls timing.
Loading and processing indicators
Use a typewriter to type status messages during a multi-step process: "Compiling...", "Optimising...", "Done!" with appropriate timing per step.
Terminal-style developer interfaces
Combine with the Aurora Background and the blinking cursor for a terminal-aesthetic hero that signals a developer-focused product.
A/B test headline variants
The words array is trivial to change. Test different keyword orders, phrase lengths, and value propositions without code changes.

Got questions?

Frequently Asked Questions

tick() checks the deleting state. When typing: el.textContent = word.slice(0, ++ci) adds one character. When ci equals word length, deleting = true and a 1800ms timeout fires. When erasing: ci decrements until 0, then wi advances via modulo and deleting resets to false.

The setTimeout in the typing branch uses 80ms per character. The erasing branch uses 50ms. Reduce these values (e.g. 40ms/30ms) for faster animation, or increase (120ms/80ms) for slower.

Remove the animation: blink ... property from .cursor in the CSS panel. The cursor will remain visible without blinking.

Yes. Add longer strings to the words array: "build delightful web experiences" is valid. Longer strings take more time to type and erase — adjust the 1800ms pause if needed.

Remove the modulo wraparound: instead of wi = (wi + 1) % words.length, use if (wi < words.length - 1) wi++; else { deleting = false; return; } to stop after the last word.

Yes. Click "JSX" for a React component. In React, manage ci, wi, and deleting in useState. Use useEffect with a setTimeout to call tick(), storing the timeout ID in useRef for cleanup on unmount.