webdevpuneet.com

Typing Indicator — Chat "..." Dots HTML CSS JS

Typing Indicator · Loaders · Plain HTML, CSS & JS · Live preview

CategoryLoaders

Copy & paste

Source Code

<div class="ty-chat">
  <div class="ty-msg ty-them">Hey! Did the deploy go through?</div>
  <div class="ty-msg ty-me">Checking now…</div>

  <div class="ty-row" id="tyRow">
    <div class="ty-avatar">A</div>
    <div class="ty-bubble" aria-label="Contact is typing">
      <span class="ty-dot"></span><span class="ty-dot"></span><span class="ty-dot"></span>
    </div>
  </div>

  <div class="ty-controls">
    <button type="button" class="ty-btn" id="tyToggle">Stop typing</button>
  </div>
</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

Delay-staggered dots
One keyframe plus animation-delay offsets creates the travelling-wave bounce.
Soft organic motion
Easing and a dip to 50% opacity at rest give the dots a natural bounce.
Message-matched bubble
Styled like an incoming message with the squared bottom-left corner and avatar.
Show / hide state
Toggle helpers reveal or collapse the indicator as a real chat would.
Auto-hide on idle
A 4s timer hides the dots if no further typing event arrives — a safety net for dropped events.
Graceful collapse
Hiding fades opacity and height instead of an abrupt display:none, so the list doesn't jump.
Event-ready API
showTyping() is exposed so you can wire it to a WebSocket or presence channel.
Drop-in & no library
A single appendable row in plain HTML/CSS/JS — zero dependencies.

About this UI Snippet

Typing Indicator — Staggered Bouncing Dots in a Chat Bubble

Screenshot of the Typing Indicator snippet rendered live

The three bouncing dots that mean "someone is typing…" are a tiny, universally understood piece of UI — and getting the animation and the show/hide logic right is what makes a chat feel alive. This snippet builds the classic typing indicator in plain HTML, CSS, and vanilla JavaScript: a bubble with three dots animated on a staggered loop, plus the show, hide, and auto-hide-on-idle behaviour a real chat needs — no library.

Three dots, one keyframe, staggered

All three dots share a single @keyframes that lifts a dot up and brightens it, then settles. The illusion of a travelling wave comes entirely from animation-delay: the second dot starts 0.18s after the first, the third 0.36s after, so at any moment they're at different points in the same cycle. This delay-stagger technique — one animation, offset start times — is the elegant way to build any sequential dot or bar loader without writing three separate animations. The easing and the dip-to-50%-opacity at rest give it the soft, organic bounce of the real thing.

A proper chat bubble

The dots sit in a bubble styled exactly like an incoming message — same background, same asymmetric border-radius with the squared bottom-left corner that marks it as "from them" — next to the sender's avatar. Matching the indicator to your message bubbles is what makes it read as "this person is about to send a message" rather than a generic spinner; it occupies the same visual slot the real message will.

Show, hide, and auto-hide-on-idle

A typing indicator isn't just an animation — it's state. The snippet includes the logic a real chat uses: showTyping() reveals the bubble and resets a 4-second idle timer that hides it automatically, so a dropped "stopped typing" event never leaves the dots stuck forever. In production you'd call showTyping() each time a typing event arrives over your socket, and hide it when the actual message lands — the timer is the safety net. Exposing showTyping on window makes it easy to wire to your real-time events.

Graceful hide transition

Hiding collapses the row with opacity and height rather than an abrupt display: none, so the indicator fades out and the conversation closes the gap smoothly — the small polish that keeps the chat from jumping when typing stops. The demo button toggles it so you can see both states immediately.

Drop-in for any chat

The markup is just a row you append to your message list, so it drops into any chat UI. Swap the avatar and bubble colours to match your theme, wire showTyping() to your WebSocket or presence channel, and you have the complete typing-indicator behaviour. It's also a clear reference for the delay-stagger animation pattern used across loaders and sequential effects.

Step by step

How to Use

  1. 1
    Paste HTML, CSS, and JSA mini chat renders with two messages and a typing indicator (three bouncing dots).
  2. 2
    Toggle itClick the button to hide or show the typing bubble and see the fade transition.
  3. 3
    Call showTyping() on eventsIn a real app, call showTyping() each time a "typing" event arrives over your socket.
  4. 4
    Let it auto-hideIf no further typing event arrives within 4s, the indicator hides itself automatically.
  5. 5
    Theme the bubbleMatch the avatar and bubble colours and radius to your chat's message styling.
  6. 6
    Drop into your listAppend the row to your message list and remove it when the real message arrives.

Real-world uses

Common Use Cases

Messaging app composing state
Show that the other person is typing, with three dots bouncing from one keyframe and staggered animation-delay values inside a chat UI.
AI assistant interfaces
Indicate that an assistant is composing a reply in an AI chat interface, styled like an incoming message bubble.
Support chat widgets
Reassure users that an agent is responding inside a floating chat widget, with auto-hide when the typing stops.
Comment and reply threads
Show a live composing state in a comment thread, using show and hide helpers that collapse the indicator cleanly.
Dots loader relatives
Compare with the dots loader for generic waiting, where this version adds a bubble shape and a dip to half opacity at rest.

Got questions?

Frequently Asked Questions

All three dots share the same @keyframes (lift up and brighten, then settle), but each starts at a different time via animation-delay — 0s, 0.18s, 0.36s. Because they're offset within the same cycle, at any instant they're at different phases, producing a travelling wave. This one-animation-plus-staggered-delays technique avoids writing three separate animations and is the standard way to build sequential dot loaders.

Typing indicators are driven by events — "started typing" and "stopped typing" / message-sent. If the stop event is dropped (flaky network, closed tab), the dots can get stuck on forever. A short idle timer that hides the indicator unless refreshed by a new typing event is the safety net every robust chat uses: showTyping() resets the timer each call, so the dots persist only while typing events keep arriving.

Call showTyping() whenever a typing event arrives over your WebSocket or presence channel (it reveals the bubble and resets the idle timer), and hide the row when the actual message arrives or the user goes idle. The snippet exposes showTyping() on window for easy wiring; in a framework you'd call a component method instead. The animation and bubble are purely presentational.

display:none removes the element instantly, which makes the message list jump as the gap closes abruptly. Animating opacity and collapsing the height (with overflow hidden and negative margin) lets the indicator fade out smoothly and the conversation close the space gracefully — the small polish that keeps the chat from feeling jumpy when typing stops.

Render the bubble conditionally on an isTyping state. In React, set isTyping from your socket handler and clear it with a useEffect timeout; in Vue, use a ref with a watcher or timeout; in Angular, a component property with a timer. The dot animation is pure CSS and ports unchanged — only the show/hide state and idle timer move into the framework.