React + MotionBotones

Píldora de carga a check

Un botón píldora que muestra un spinner mientras trabaja y termina con un check que entra con muelle.

Ajustes

1200ms

Código

import { useState } from "react";
import { AnimatePresence, motion } from "framer-motion";
import { Check, Loader2 } from "lucide-react";

const spring = { type: "spring", stiffness: 300, damping: 30 } as const;

interface LoadingCheckButtonProps {
  text?: string;
  color?: string;
  /** Duración de la carga simulada en ms. */
  delay?: number;
}

export default function LoadingCheckButton({
  text = "Reservar",
  color = "#0071e3",
  delay = 1200,
}: LoadingCheckButtonProps) {
  const [state, setState] = useState<"idle" | "load" | "done">("idle");

  const go = () => {
    if (state !== "idle") return;
    setState("load");
    setTimeout(() => setState("done"), delay);
    setTimeout(() => setState("idle"), delay + 1600);
  };

  return (
    <motion.button
      type="button"
      whileTap={{ scale: 0.96 }}
      onClick={go}
      className="flex min-w-36 items-center justify-center gap-2 rounded-full px-6 py-3 text-sm font-medium text-white shadow-[0_1px_2px_rgba(0,0,0,0.03),0_8px_28px_rgba(0,0,0,0.05)]"
      style={{ backgroundColor: color }}
    >
      <AnimatePresence mode="wait" initial={false}>
        {state === "idle" ? (
          <motion.span
            key="idle"
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
            exit={{ opacity: 0 }}
          >
            {text}
          </motion.span>
        ) : state === "load" ? (
          <motion.span key="load" initial={{ scale: 0.6 }} animate={{ scale: 1 }}>
            <Loader2 className="h-4 w-4 animate-spin" />
          </motion.span>
        ) : (
          <motion.span
            key="done"
            initial={{ scale: 0.4, opacity: 0 }}
            animate={{ scale: 1, opacity: 1 }}
            transition={spring}
            className="flex items-center gap-2"
          >
            <Check className="h-4 w-4" /> Listo
          </motion.span>
        )}
      </AnimatePresence>
    </motion.button>
  );
}

Prompt para IA

Pégalo en ChatGPT, Claude o Cursor y adaptará el bloque a tu proyecto con estos ajustes, aunque no uses React.

Más en Botones

Dónde usarlo

Hay acciones que piden un «ya está» bien claro: reservar mesa, pagar un pedido o apuntarse a una clase del gimnasio. Esta píldora pasa por una espera corta y acaba con un check que entra rebotando, sin dejar dudas. Ese rebote final convierte un simple botón de carga en una animación de éxito que se nota.

Ajusta la duración a lo que tarda de verdad tu petición, normalmente menos de un segundo y medio. Si puede alargarse mucho más, añade al lado un texto como «Confirmando…» para que la espera no parezca atascada.