Documentation v0.1.1

01 · Start here

Quick start

Generative Loaders is a React component library for the waiting states unique to generative products. It requires React 18 or newer and Node 20 or newer.

npm install generative-loaders

Import the component and the stylesheet once in your app:

import { TextLoader } from "generative-loaders";
import "generative-loaders/styles.css";

export function Answer({ text }: { text: string }) {
  return <TextLoader text={text} variant="decode" />;
}
The essential detail

Pass the complete response received so far—not only the newest token. The loader detects the new suffix and animates it while keeping earlier text stable.

02 · Integration

Streaming text

Start with an empty string and append decoded chunks as they arrive. Replace the endpoint with your own streaming route.

"use client";

import { useState } from "react";
import { TextLoader } from "generative-loaders";
import "generative-loaders/styles.css";

export function StreamingAnswer() {
  const [text, setText] = useState("");

  async function generate() {
    setText("");
    const response = await fetch("/api/generate", { method: "POST" });
    if (!response.ok || !response.body) throw new Error("Generation failed");

    const reader = response.body.getReader();
    const decoder = new TextDecoder();
    while (true) {
      const { value, done } = await reader.read();
      if (done) break;
      setText((current) => current + decoder.decode(value, { stream: true }));
    }
  }

  return <TextLoader text={text} variant="cascade" />;
}

03 · Components

Choose the right primitive

Text

TextLoader

For response text that grows over time. Only the newly received suffix animates.

Inline

InlineLoader

For buttons, status rows, and the short wait before any response arrives.

Image

ImageLoader

For a reserved square frame while an image is being generated.

Examples

<TextLoader text={streamedText} variant="decode" color="#7c3aed" />

<span>
  <InlineLoader variant="orbit" /> Generating response…
</span>

<ImageLoader
  variant="tiles"
  size={192}
  radius={24}
  label="Generating product image"
/>

Text variantsdecode · typewriter · skeleton · cascade · focus · wipe · flip · redact · line · terminal · wave · dissolve · slice · tracking · coalesce · fragments

Inline variantsglyph · matrix · orbit · ripple · signal · spark · rotor · pixel-drift · chomp · snake · fold · gravity · domino · aperture · dot-pulse · vortex · halo · count-up

Image variantsskeleton · bands · tiles · scan · pixel-grid · resolution · coalesce · diffusion · raster · bloom · focus · shutter

04 · Reference

Props

TextLoader

PropTypeDefaultPurpose
textstringrequiredComplete response received so far.
variantTextLoaderVariantrequiredVisual reveal treatment.
colorCSS color#111111Loader and text color.
speedpositive number1Animation speed multiplier.
pausedbooleanfalseStops motion without removing content.
classNamestringCustom class on the root element.
aria-labelstringnormalized textOverrides announced status text.

InlineLoader

Requires variant. Also accepts size (default 1.15em), color (default currentColor), speed, paused, className, and optional label.

ImageLoader

Requires variant. Also accepts size (default 10rem), radius (default 10%), color, speed, paused, className, and label (default “Generating image”). Numeric size and radius values are treated as pixels.

05 · Accessibility

Accessible by default

  • TextLoader uses a polite live status and exposes the received text while its visual layers remain hidden from assistive technology.
  • InlineLoader is hidden from assistive technology when no label is supplied. This prevents duplicate announcements when adjacent copy already says “Generating”. Add label when it stands alone.
  • ImageLoader announces “Generating image” by default. Use a more specific label when the context benefits from it.
  • All loaders respect the user’s reduced-motion preference and retain their meaning without animation.

06 · Styling

Styling and behavior

Use props for color, size, radius, and speed. Use className for layout concerns such as margins and alignment. Import the packaged stylesheet exactly once near your application root.

.answer-loader {
  display: block;
  max-width: 42rem;
  font: 500 1.125rem/1.65 system-ui, sans-serif;
}

The package is SSR-safe. Components that animate use client-side React behavior, but they render stable markup on the server. TypeScript types for every prop and variant are exported from the package root.

07 · Help

Common pitfalls

The loader is unstyled

Import generative-loaders/styles.css once in your root layout or application entry file.

Every update re-animates the full response

Append chunks to one string. Replacing earlier text or remounting the component with a changing key resets suffix detection.

The inline status is not announced

Add a label when the loader has no adjacent visible status copy. Leave it unset when nearby text already communicates the same activity.

Animation speed behaves unexpectedly

Use a finite positive number. Invalid, zero, and negative values safely fall back to 1.

Still stuck?

Open an issue with a small reproduction.

GitHub30.3k