About
A small working pill for AI replies – an eight-ray spinner and a shimmering label that rolls through phases and settles on a check. From affectUI (affectui.com).
Built with React and Tailwind. Copy the prompt and paste it into Claude Code, Cursor or Lovable: your agent adds Thinking Loader to your project and fits it to your theme.
Code
How Thinking Loader is used: the demo's code. The component's own source comes with a free account.
Usage.tsx
import { useEffect, useState } from "react";
import { ThinkingLoader } from "@/components/ui/thinking-loader";
const PHASES = ["Thinking", "Searching the web", "Reading 12 sources", "Comparing pricing", "Writing response"];
const INTERVAL = 2000;
const HOLD = 2600;
// Walks through the phases, finishes, holds the check for a moment and starts over
export default function Demo() {
const [run, setRun] = useState(0);
const [done, setDone] = useState(false);
useEffect(() => {
const finish = window.setTimeout(() => setDone(true), PHASES.length * INTERVAL);
const again = window.setTimeout(() => {
setDone(false);
setRun((r) => r + 1);
}, PHASES.length * INTERVAL + HOLD);
return () => {
window.clearTimeout(finish);
window.clearTimeout(again);
};
}, [run]);
return (
<div className="tl-demo">
<style>{demoCss}</style>
<div className="tl-demo__chat">
<p className="tl-demo__you">Which CRM fits a 10-person sales team best?</p>
<ThinkingLoader key={run} phases={PHASES} interval={INTERVAL} done={done} />
</div>
</div>
);
}
const demoCss = `
.tl-demo {
display: grid;
grid-template-columns: minmax(0, 420px);
place-content: center;
width: 100%;
font-family: "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}
.tl-demo__chat {
display: grid;
justify-items: start;
gap: 18px;
min-width: 0;
}
.tl-demo__you {
justify-self: end;
max-width: 88%;
margin: 0;
padding: 10px 14px;
border-radius: 18px 18px 6px 18px;
background: #f0f0f0;
color: #171717;
font-size: 14px;
line-height: 1.45;
}
:where(.dark, [data-theme="dark"]) .tl-demo__you { background: #262628; color: #ededed; }
/* Library cards: just the pill, a little larger and dead center (the question would push it off-center) */
.card__preview .tl-demo {
position: absolute;
inset: 0;
grid-template-columns: auto;
place-content: center;
padding-top: 20px;
}
.card__preview .tl-demo__you { display: none; }
.card__preview .tl-demo__chat { justify-items: center; zoom: 1.25; }
/* A fixed stage – only the pill changes width */
.stage:has(.tl-demo) { height: 420px; }
@media (max-width: 760px) {
.stage:has(.tl-demo) { height: 380px; padding-left: 16px; padding-right: 16px; }
}
/* Clean style: the question bubble in the clean type scale */
.tl-demo--clean .tl-demo__you {
padding: 8px 12px;
border-radius: 10px 10px 4px 10px;
background: rgba(0, 0, 0, 0.04);
color: rgba(0, 0, 0, 0.88);
font-size: 13px;
line-height: 20px;
}
:where(.dark, [data-theme="dark"]) .tl-demo--clean .tl-demo__you { background: rgba(255, 255, 255, 0.08); color: rgba(255, 255, 255, 0.9); }
`;












