Source Code

<div class="loader-card" id="loader-card">
  <div class="loader-head">
    <div class="ai-dot" id="ai-dot"></div>
    <p class="stage-text" id="stage-text" aria-live="polite">Analyzing request…</p>
  </div>

  <div class="shimmer-lines" id="shimmer-lines">
    <div class="shimmer-line" style="width: 96%;"></div>
    <div class="shimmer-line" style="width: 88%;"></div>
    <div class="shimmer-line" style="width: 92%;"></div>
    <div class="shimmer-line" style="width: 60%;"></div>
  </div>

  <button class="cancel-btn" id="cancel-btn">Cancel generation</button>

  <div class="done-state" id="done-state">
    <div class="done-icon">&#10003;</div>
    <p class="done-text">Response generated.</p>
    <button class="restart-btn" id="restart-btn">Generate again</button>
  </div>
</div>

AI Generating Content Loader — Free HTML CSS JS Snippet

AI Generating Content Loader · Loaders · Plain HTML, CSS & JS · Live preview

What's included

Features

Text-shaped shimmer placeholder: four variable-width bars mimic paragraph line lengths, not a generic spinner
Animated light-sweep via linear-gradient background-position keyframe on each shimmer line
Staggered grow-in animation suggests progressive left-to-right streaming as lines appear
Cycling staged status text ("Analyzing…", "Drafting…", "Finalizing…") instead of one static label
Cross-fade transition between stage text changes avoids an abrupt text swap
Always-available Cancel control that genuinely stops the timer, not just hides the UI
Distinct cancelled-state view proves the generation actually halted rather than continuing invisibly
Explicit completion state with restart control closes the loop once generation finishes

About this UI Snippet

AI Generating Content Loader — Streaming Shimmer Placeholder with Staged Status Text and Cancel Control

Screenshot of the AI Generating Content Loader snippet rendered live

A generic spinner tells a user almost nothing except "wait." For a short network request that is usually fine, but AI generation — drafting a response, writing code, producing an image — routinely takes several seconds to tens of seconds, and an indefinite spin over that duration reads as the product being stuck rather than working. This snippet builds a loading state purpose-built for AI generation moments: a shimmering placeholder shaped like the content actually being produced, a status line that cycles through real, specific stages of the work, and a cancel control, because any generation that can run for more than a couple of seconds should always be interruptible.

Why the placeholder is shaped like text, not a generic spinner

Instead of a circular spinner, this snippet renders four .shimmer-line bars of varying widths (96%, 88%, 92%, 60%) styled to resemble the ragged right edge of real paragraph text. Each bar carries a moving linear-gradient background animated via background-position in the shimmer-sweep keyframe, producing the classic light-sweep shimmer effect widely recognised as "content is being prepared here." Layering a staggered grow-in animation on top — each line scaling from scaleX(0) to scaleX(1) with an increasing animation-delay per line — makes the lines appear to stream in progressively left-to-right, echoing how AI-generated text actually appears token by token in a real streaming response. The combined effect communicates "text is forming" far more specifically than a spinner ever could, setting an accurate expectation for what the user is about to receive.

Staged status text instead of a single static label

Beneath a small pulsing indicator dot, the #stage-text element cycles through an array of realistic phases — Analyzing request…, Drafting response…, Refining wording…, Finalizing… — advancing on a setInterval timer and cross-fading between each via a brief opacity transition rather than snapping instantly. Even though these stages are simulated here rather than tied to a real backend event stream, the underlying principle applies directly to production systems: whenever your AI pipeline actually does pass through distinguishable phases (retrieval, generation, post-processing, safety filtering), surfacing that real progress as text is far more informative than a label that never changes for the entire wait. A status line that visibly moves forward reassures the user that work is genuinely happening, not that the request has silently stalled.

Cancellation is not optional

The cancelBtn click handler sets a cancelled flag, clears the running stageTimer interval, and immediately swaps the card into a distinct cancelled-state view — the generation genuinely stops rather than merely hiding the loading UI while work continues invisibly in the background. Any operation whose duration is unpredictable and can run into the tens of seconds must give the user an escape hatch; forcing someone to wait out a generation they no longer want, with no way to abort it, is a common and avoidable source of frustration in AI-native products. The finishGeneration() function checks the cancelled flag before rendering the completed state precisely so a late-arriving completion cannot override a cancellation the user already issued.

Why this matters for 2026 AI-native UX

Communicating real progress rather than spinning indefinitely is one of the defining expectations of 2026 AI-native interface design — as generation times grow with more capable, more deliberate models, the gap between "a spinner that could mean anything" and "a status line that tells you what is actually happening" becomes the difference between a product that feels trustworthy under load and one that feels broken. Pairing an honest progress signal with an always-available cancel option treats the user's time and attention as something the AI system respects, rather than something it can indefinitely consume without recourse.

Build with AI

Build, Understand, Optimize, and Extend It With AI

Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly how the shimmer-sweep gradient animation and the staggered grow-in transform combine to suggest text streaming in progressively, versus how a plain opacity pulse would read differently to a user. It's a good exercise to ask the assistant to wire the simulated STAGES array to a real EventSource or fetch stream so the status text reflects genuine backend progress instead of a fixed timer, and to connect the Cancel button to an AbortController that actually terminates the underlying network request rather than only stopping local UI updates. You could also ask it to adapt the text-line shimmer into an image-generation variant using a single pulsing gradient block sized to a target aspect ratio, while keeping the same staged-text-plus-cancel structure intact.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a loading state specifically for "AI is generating content" moments in plain HTML, CSS, and JavaScript — not a generic spinner.

Requirements:
- Render a shimmering placeholder shaped like the content being generated: several bars of varying width that resemble lines of paragraph text (for a text-generation use case), using an animated gradient sweep so it reads as active work in progress rather than a static gray block.
- Make the placeholder lines appear to reveal progressively, left to right or top to bottom, with a staggered animation delay per line, to suggest content streaming in rather than all appearing simultaneously.
- Show a small status line below or near the placeholder that cycles through at least three realistic, specific phase labels (for example "Analyzing request…", "Drafting response…", "Finalizing…") on a timer, cross-fading between each change rather than swapping text instantly.
- Include a persistently visible Cancel control that, when clicked, immediately stops the stage-cycling timer and any pending completion, and shows a clearly distinct "cancelled" state so the user can visually confirm the generation actually stopped rather than continuing invisibly in the background.
- When the full stage sequence completes without cancellation, transition to an explicit completed state (for example a checkmark and confirmation text) and offer a way to restart the generation sequence for demo purposes.
- Ensure the cancelled state and the completed state are visually distinct from each other and from the in-progress state, so at a glance the user always knows which of the three states they are looking at.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Step by step

How to Use

  1. 1
    Watch the generation sequence play automaticallyOn load, startGeneration() begins immediately: the four shimmer lines grow in left-to-right with a staggered animation-delay, and the stage text below the pulsing dot cycles through "Analyzing request…", "Drafting response…", "Refining wording…", and "Finalizing…" before completing.
  2. 2
    Let it finish to see the completed stateAfter the last stage, finishGeneration() swaps the card to a green checkmark and "Response generated." message. Click "Generate again" to replay the full sequence from the beginning via restartBtn's click handler.
  3. 3
    Click "Cancel generation" mid-run to interrupt itClicking cancel at any point during generation immediately clears the running stage interval and shows a distinct grey "Generation cancelled." state — proving the work actually stops rather than just hiding the loader while a timer keeps running invisibly underneath.
  4. 4
    Inspect the shimmer-sweep and grow-in animationsIn the CSS panel, .shimmer-line uses a linear-gradient with animated background-position for the light-sweep, layered with a scaleX(0) to scaleX(1) grow-in keyframe with a staggered animation-delay per line to suggest progressive left-to-right streaming.
  5. 5
    Customize the stage text for your real pipelineEdit the STAGES array in the JS panel to match your actual backend phases — for example "Searching your documents…", "Generating summary…", "Checking sources…" for a RAG pipeline. If your backend emits real progress events, replace the fixed setInterval timing with updates driven by those events for genuinely accurate progress.
  6. 6
    Export and connect to a real streaming responseClick HTML or JSX to export. Replace the shimmer lines with your actual rendered text as tokens stream in from a real API (for example via Server-Sent Events or a ReadableStream), keeping the same progressive reveal styling, and wire the cancel button to an AbortController tied to your fetch request so cancellation genuinely stops the network call.

Real-world uses

Common Use Cases

Chat assistants and AI writing tools waiting on a response
Any product where a user submits a prompt and waits for an LLM-generated reply benefits from replacing a generic spinner with this shimmer-plus-staged-text pattern, since typical response times of several seconds feel meaningfully shorter and less uncertain when the interface visibly communicates what phase of generation is underway.
AI image and media generation with longer wait times
Image, video, and audio generation frequently takes tens of seconds to minutes. Swap the text-shimmer lines for a soft pulsing gradient block matching the target image's aspect ratio, and extend the STAGES array to reflect real pipeline phases like "Composing scene…", "Rendering details…", "Upscaling…" so the wait feels accounted for rather than opaque.
RAG pipelines and multi-step agent tool calls
Retrieval-augmented generation and agentic tool-calling flows genuinely do pass through distinguishable phases — searching, retrieving, synthesizing, verifying. Replace the simulated setInterval timing with real progress events from your backend so each stage-text update reflects an actual completed step rather than an estimate, giving users accurate rather than merely reassuring feedback.
Design systems standardizing "AI is working" states across a product
Products with several distinct AI features (chat, summarization, image generation, code completion) benefit from one shared loader component with a configurable shimmer shape and stage-text array, so every AI wait state in the product communicates progress consistently rather than each team building its own spinner, echoing the gated, transparent pattern taught in the AI Agent Action Approval Card for the step that typically follows generation.
Teaching progress-communicating loading states over indefinite spinners
This snippet is a compact reference for the broader principle that any operation whose duration is unpredictable and potentially long should communicate real or realistic progress rather than spin indefinitely, and should always remain interruptible — the same checklist applies to file uploads, long-running exports, and background jobs well beyond AI generation specifically.
Streaming token-by-token text rendering with an honest fallback state
When your backend genuinely streams tokens (via Server-Sent Events or a ReadableStream), use this shimmer state only for the brief gap before the first token arrives, then swap immediately to rendering real streaming text as it comes in — keeping the shimmer purely as an honest "nothing has arrived yet" signal rather than a decorative animation that runs alongside real content.
Related: Auto-Retry Loader with Countdown and Manual Retry
See the Auto-Retry Loader with Countdown and Manual Retry for a related loaders pattern worth pairing with this one.
Related: Barcode Scan Sweep Loader
See the Barcode Scan Sweep Loader for a related loaders pattern worth pairing with this one.
Related: AI Token Flicker Typing Loader
See the AI Token Flicker Typing Loader for a related loaders pattern worth pairing with this one.
Related: Heartbeat Pulse Monitor Loader
See the Heartbeat Pulse Monitor Loader for a related loaders pattern worth pairing with this one.

Got questions?

Frequently Asked Questions

A spinner communicates only that something is happening, with no indication of what or how much progress has been made, which feels increasingly uncertain the longer an AI generation takes. A placeholder shaped like the actual content being produced — lines of varying width mimicking paragraph text, or a pulsing block matching an image's aspect ratio — sets an accurate expectation for what is about to appear and, combined with a progressive reveal animation, visually echoes how AI output genuinely streams in token by token.

In this demo the stages advance on a fixed timer for illustration, but the pattern is designed to be replaced with real backend progress events in production — for example a RAG pipeline emitting an actual "retrieval complete" event before starting synthesis. Even a well-designed simulated sequence is more informative than a static label, but wiring it to genuine pipeline phases whenever your backend can expose them is strictly better and avoids ever showing a stage that does not correspond to real work.

Generation times are inherently unpredictable and can run into the tens of seconds or longer, and a user may realize partway through that they mistyped their prompt, changed their mind, or need to leave immediately. Forcing them to wait out a generation with no way to stop it wastes their time and, for products billing by token or compute usage, needlessly consumes resources on output nobody wants — an always-visible, immediately effective cancel control avoids both problems.

In this demo, cancelling clears the setInterval driving the stage cycle so no further UI updates occur. In a real implementation backed by a fetch() call or EventSource, pass an AbortController's signal into the request and call controller.abort() from the cancel handler — this actually terminates the network request and, for a properly implemented backend, stops the AI provider from continuing to generate and bill for tokens the user no longer wants.

Yes — replace the .shimmer-lines text bars with a single .shimmer-block styled to the target media's aspect ratio (for example aspect-ratio: 1/1 for a square image), using the same linear-gradient sweep animation for the shimmer effect, or a slow pulsing opacity/scale animation to suggest an image forming. Keep the staged status text and cancel button unchanged, updating STAGES to reflect image-specific phases like "Composing scene…" or "Rendering details…".