nextui.dev
Home
Library
Docs
Pricing
Components26
AI & agents5
  • AI chat inputPro
  • Agent task checklistPro
  • Agent thinking
  • Thinking indicator
  • Prompt deckNew
Forms & inputs7
  • Input with label
  • Input with icon and button
  • Settings switches
  • Password with strength meter
  • One-time code input
  • WaitlistNew
  • File dropzonePro
Cards5
  • Style marquee cardPro
  • Folder card
  • Celebration cardPro
  • Smart PDF stackNew
  • Waitlist pass
Buttons, badges & avatars5
  • Buttons with icons
  • QR scan badge
  • Avatar group
  • Rolling price badge
  • Avatars with status
Feedback & display4
  • Alert banners
  • FAQ accordion
  • Keyboard shortcuts
  • Empty state
Blocks11
Hero4
  • Animated pill heroPro
  • Integrations grid hero
  • Waitlist heroNew
  • Orbit chat heroNew
Features & pricing3
  • Usage cardPro
  • Three-tier pricing
  • Feature with board
Testimonials & team3
  • Testimonial cards
  • Single quote
  • Team carousel
Sign-in1
  • Sign-in cardPro
Motion2
Effects2
  • Text revealPro
  • Sound ringNew
Themes9
  • Default
  • Ember
  • Ocean
  • Forest
  • Grape
  • Rose
  • Amber
  • Clay
  • Brutal
nextui.dev

nextui components, blocks and themes you install with one command.

Product

  • Components
  • Blocks
  • Motion
  • Themes
  • Pricing

Resources

  • Docs
  • Roadmap
  • GitHub

Legal

  • License
  • Privacy
  • Terms
  • Refunds

nextUI

[ 200 OK ] · nextUI

© 2026 NEXTUI.DEV. All rights reserved.

ComponentsAI & agentsThinking indicator

Thinking indicator

AI thinking status with a monochrome sparkling diamond and shimmering text.

Copy prompt
MCP

Installation

Runs as is, no setup needed. Want a shorter command like @nextui/button-03? See short names.

$pnpm dlx shadcn@latest add https://tinistudio.vercel.app/r/button-03.json

Related

All components
Pro

AI chat inputNew

Rounded AI prompt box with attach tools, a round send button and a shimmering "Searching the web" status below.

Pro

Agent task checklistNew

Collapsible to-do list for an AI agent: each step fills a progress ring, rolls its percentage and strikes through when done.

Agent thinking

Thinking indicator: a wobbling dot ring and a shimmering label.

npx shadcn@latest add https://tinistudio.vercel.app/r/button-03.json
components/nextui/button-03.tsx
"use client";

import { useEffect, useRef, useState } from "react";
import { motion, useReducedMotion } from "motion/react";
import { cn } from "@/lib/utils";

// Brilliant-cut diamond: wide table on top, crown down to the girdle, pavilion down to the culet.
// Monochrome shades: the theme's ink mixed toward white, so the stone stays bright in light and dark.
const ink = (n: number) => `color-mix(in oklab, var(--foreground) ${n}%, white)`;
const FACETS = [
  // crown
  { points: "2,15 11,7 10,15", shade: ink(32) },
  { points: "11,7 29,7 30,15 10,15", shade: ink(10) },
  { points: "29,7 38,15 30,15", shade: ink(45) },
  // pavilion
  { points: "2,15 10,15 20,37", shade: ink(22) },
  { points: "10,15 30,15 20,37", shade: ink(38) },
  { points: "30,15 38,15 20,37", shade: ink(55) },
];
const SWEEP = 2.4; // seconds for the light to cross every facet once

function Diamond({ className }: { className?: string }) {
  return (
    <svg viewBox="0 0 40 40" className={cn("shrink-0", className)} aria-hidden>
      {FACETS.map(({ points, shade }) => (
        <polygon key={points} points={points} style={{ fill: shade, stroke: shade }} strokeWidth={0.3} strokeLinejoin="round" />
      ))}
      {/* Light catches one facet after another, so the stone sparkles. */}
      {FACETS.map(({ points }, i) => (
        <polygon
          key={`hi-${points}`}
          points={points}
          fill="white"
          className="button-03-facet"
          style={{ animationDelay: `${(i * SWEEP) / FACETS.length}s` }}
        />
      ))}
    </svg>
  );
}

export default function Button03() {
  const reduce = useReducedMotion();
  const ref = useRef<HTMLDivElement>(null);
  const [visible, setVisible] = useState(false);
  const play = visible && !reduce;

  // Loops only run while the indicator is on screen.
  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const io = new IntersectionObserver(([entry]) => setVisible(entry.isIntersecting));
    io.observe(el);
    return () => io.disconnect();
  }, []);

  return (
    <div
      ref={ref}
      role="status"
      data-paused={play ? undefined : ""}
      className="button-03 flex items-center justify-center gap-2.5 text-sm font-medium"
    >
      <Diamond className="size-5" />
      {/* Same shimmer as card-02. */}
      <motion.span
        className="bg-[linear-gradient(90deg,var(--muted-foreground)_0%,var(--muted-foreground)_40%,var(--foreground)_50%,var(--muted-foreground)_60%,var(--muted-foreground)_100%)] bg-[length:250%_100%] bg-clip-text text-transparent"
        initial={{ backgroundPosition: "100% 0" }}
        animate={play ? { backgroundPosition: ["100% 0", "0% 0"] } : undefined}
        transition={{ duration: 2, ease: "linear", repeat: Infinity, repeatDelay: 0.3 }}
      >
        Thinking...
      </motion.span>

      <style>{`
        .button-03-facet {
          opacity: 0;
          animation: button-03-facet ${SWEEP}s ease-in-out infinite;
        }
        .button-03[data-paused] .button-03-facet { animation-play-state: paused; }
        @keyframes button-03-facet {
          0%, 35%, 100% { opacity: 0; }
          12% { opacity: 0.85; }
        }
        @media (prefers-reduced-motion: reduce) {
          .button-03-facet { animation: none; }
        }
      `}</style>
    </div>
  );
}
Sign in