Botón de carga con éxito
Al pulsarlo, el texto se convierte en un spinner y luego en un check con «Hecho», antes de volver a empezar.
Ajustes
Código
import { useState } from "react";
import { AnimatePresence, motion } from "framer-motion";
import { Check, Loader2 } from "lucide-react";
interface LoadingButtonProps {
color?: string;
radius?: number;
text?: string;
/** Multiplica la velocidad de la carga simulada. */
intensity?: number;
}
export default function LoadingButton({
color = "#0071e3",
radius = 24,
text = "Guardar cambios",
intensity = 1,
}: LoadingButtonProps) {
const [state, setState] = useState<"idle" | "loading" | "success">("idle");
return (
<motion.button
type="button"
className="flex min-w-36 items-center justify-center gap-2 px-6 py-3 text-sm font-semibold text-white shadow-lg"
style={{
backgroundColor: state === "success" ? "#0071e3" : color,
borderRadius: radius,
}}
animate={{ scale: state === "loading" ? 0.97 : 1 }}
onClick={() => {
if (state !== "idle") return;
setState("loading");
setTimeout(() => setState("success"), 1500 / intensity);
setTimeout(() => setState("idle"), 1500 / intensity + 1800);
}}
>
<AnimatePresence mode="wait" initial={false}>
{state === "idle" && (
<motion.span
key="idle"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
>
{text}
</motion.span>
)}
{state === "loading" && (
<motion.span
key="loading"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
>
<Loader2 className="h-4 w-4 animate-spin" />
</motion.span>
)}
{state === "success" && (
<motion.span
key="success"
initial={{ opacity: 0, scale: 0.5 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0 }}
className="flex items-center gap-1.5"
>
<Check className="h-4 w-4" /> Hecho
</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
Si no pasa nada, la gente vuelve a hacer clic. Este botón responde en el acto: enseña que trabaja y luego confirma. Encaja en el «Guardar cambios» de unos ajustes, el «Enviar» de un contacto o el «Aplicar cupón» de un checkout. Reposo, carga y hecho: los tres estados que casi todo formulario le pide a un botón de carga.
En la demo la espera es simulada, así que conecta el estado de éxito a tu petición real. Si guardar puede fallar, añade también un estado de error: enseñar un check cuando no se ha guardado nada es peor que no animar.