Remco

heartbeat loader

Component

About

A feature rich loader component. Various props such as full page which is handy for a loading.tsx, easing, color, size. Accessible and performant.

Built with React and Tailwind. Copy the prompt and paste it into Claude Code, Cursor or Lovable: your agent adds heartbeat loader to your project and fits it to your theme.

Code

How heartbeat loader is used: the demo's code. The component's own source comes with a free account.

Usage.tsx

"use client"

import { useState, useEffect } from "react"
import { Heartbeat } from "@/components/ui/heartbeat-loader"

const COLORS = ["#ff4d4f", "#3b82f6", "#10b981", "#facc15", "#8b5cf6", "#ec4899"]

export default function Page() {
  const [rainbow, setRainbow] = useState(false)
  const [color, setColor] = useState("#ff4d4f")
  const [duration, setDuration] = useState(1.4)

  // Rainbow effect: cycle colors automatically
  useEffect(() => {
    if (!rainbow) return
    const interval = setInterval(() => {
      const next = COLORS[Math.floor(Math.random() * COLORS.length)]
      setColor(next)
    }, 500)
    return () => clearInterval(interval)
  }, [rainbow])

  return (
    <div className="min-h-screen bg-zinc-950 text-zinc-50">
      <div className="max-w-7xl mx-auto px-4 py-16 sm:px-6 lg:px-8">
        {/* Header */}
        <div className="text-center mb-16">
          <h1 className="text-5xl font-bold mb-4 bg-gradient-to-r from-red-400 via-pink-400 to-purple-400 bg-clip-text text-transparent">
            Heartbeat Loader
          </h1>
          <p className="text-zinc-400 text-lg">A beautiful, customizable loading animation</p>
        </div>

        {/* Interactive Controls */}
        <div className="mb-16 bg-zinc-900 rounded-2xl p-8 border border-zinc-800 shadow-2xl">
          <h2 className="text-2xl font-semibold mb-6 text-zinc-50">Interactive Demo</h2>

          <div className="grid md:grid-cols-2 gap-8">
            {/* Controls */}
            <div className="space-y-6">
              <div>
                <label className="block text-sm font-medium text-zinc-300 mb-3">
                  Animation Duration: <span className="text-red-400 font-mono">{duration.toFixed(1)}s</span>
                </label>
                <input
                  type="range"
                  min={0.5}
                  max={5}
                  step={0.1}
                  value={duration}
                  onChange={(e) => setDuration(Number.parseFloat(e.target.value))}
                  className="w-full h-2 bg-zinc-800 rounded-lg appearance-none cursor-pointer accent-red-500"
                />
              </div>

              <div>
                <label className="block text-sm font-medium text-zinc-300 mb-3">
                  Front Color: <span className="text-red-400 font-mono">{color}</span>
                </label>
                <div className="flex gap-3 items-center">
                  <input
                    type="color"
                    value={color}
                    onChange={(e) => setColor(e.target.value)}
                    disabled={rainbow}
                    className="h-12 w-20 rounded-lg cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed border-2 border-zinc-700"
                  />
                  <div className="flex-1 h-12 rounded-lg border-2 border-zinc-700" style={{ backgroundColor: color }} />
                </div>
              </div>

              <div className="flex items-center gap-3 p-4 bg-zinc-800/50 rounded-lg">
                <input
                  type="checkbox"
                  id="rainbow"
                  checked={rainbow}
                  onChange={(e) => setRainbow(e.target.checked)}
                  className="w-5 h-5 rounded accent-purple-500 cursor-pointer"
                />
                <label htmlFor="rainbow" className="text-sm font-medium text-zinc-300 cursor-pointer select-none">
                  Rainbow Mode
                </label>
              </div>
            </div>

            {/* Live Preview */}
            <div className="bg-black/20 rounded-xl border border-zinc-800 flex items-center justify-center min-h-[280px]">
              <Heartbeat frontStrokeColor={color} animationDuration={duration} width={80} height={60} strokeWidth={4} />
            </div>
          </div>
        </div>

        {/* Variants Grid */}
        <div className="grid md:grid-cols-2 lg:grid-cols-3 gap-6 mb-16">
          {/* Default */}
          <div className="bg-zinc-900 rounded-xl p-6 border border-zinc-800 hover:border-red-500/50 transition-colors">
            <h3 className="text-lg font-semibold mb-4 text-zinc-50">Default</h3>
            <div className="bg-black/20 rounded-lg h-32 flex items-center justify-center">
              <Heartbeat />
            </div>
            <code className="text-xs text-zinc-500 mt-3 block">{"<Heartbeat />"}</code>
          </div>

          {/* Blue */}
          <div className="bg-zinc-900 rounded-xl p-6 border border-zinc-800 hover:border-blue-500/50 transition-colors">
            <h3 className="text-lg font-semibold mb-4 text-zinc-50">Blue Variant</h3>
            <div className="bg-black/20 rounded-lg h-32 flex items-center justify-center">
              <Heartbeat frontStrokeColor="#3b82f6" backStrokeColor="rgba(59, 130, 246, 0.2)" />
            </div>
            <code className="text-xs text-zinc-500 mt-3 block">frontStrokeColor="#3b82f6"</code>
          </div>

          {/* Green */}
          <div className="bg-zinc-900 rounded-xl p-6 border border-zinc-800 hover:border-green-500/50 transition-colors">
            <h3 className="text-lg font-semibold mb-4 text-zinc-50">Green Variant</h3>
            <div className="bg-black/20 rounded-lg h-32 flex items-center justify-center">
              <Heartbeat frontStrokeColor="#10b981" backStrokeColor="rgba(16, 185, 129, 0.2)" />
            </div>
            <code className="text-xs text-zinc-500 mt-3 block">frontStrokeColor="#10b981"</code>
          </div>

          {/* Small */}
          <div className="bg-zinc-900 rounded-xl p-6 border border-zinc-800 hover:border-purple-500/50 transition-colors">
            <h3 className="text-lg font-semibold mb-4 text-zinc-50">Small Size</h3>
            <div className="bg-black/20 rounded-lg h-32 flex items-center justify-center">
              <Heartbeat width={40} height={30} strokeWidth={2} />
            </div>
            <code className="text-xs text-zinc-500 mt-3 block">
              width={"{40}"} height={"{30}"}
            </code>
          </div>

          {/* Large */}
          <div className="bg-zinc-900 rounded-xl p-6 border border-zinc-800 hover:border-pink-500/50 transition-colors">
            <h3 className="text-lg font-semibold mb-4 text-zinc-50">Large Size</h3>
            <div className="bg-black/20 rounded-lg h-32 flex items-center justify-center">
              <Heartbeat width={96} height={72} strokeWidth={4} />
            </div>
            <code className="text-xs text-zinc-500 mt-3 block">
              width={"{96}"} height={"{72}"}
            </code>
          </div>

          {/* Fast */}
          <div className="bg-zinc-900 rounded-xl p-6 border border-zinc-800 hover:border-yellow-500/50 transition-colors">
            <h3 className="text-lg font-semibold mb-4 text-zinc-50">Fast Animation</h3>
            <div className="bg-black/20 rounded-lg h-32 flex items-center justify-center">
              <Heartbeat animationDuration={0.7} frontStrokeColor="#facc15" backStrokeColor="rgba(250, 204, 21, 0.2)" />
            </div>
            <code className="text-xs text-zinc-500 mt-3 block">animationDuration={"{0.7}"}</code>
          </div>
        </div>

        {/* Props Table */}
        <div className="bg-zinc-900 rounded-2xl p-8 border border-zinc-800 shadow-2xl">
          <h2 className="text-2xl font-semibold mb-6 text-zinc-50">Available Props</h2>
          <div className="overflow-x-auto">
            <table className="w-full text-sm">
              <thead>
                <tr className="border-b border-zinc-800">
                  <th className="text-left py-3 px-4 text-zinc-300 font-semibold">Prop</th>
                  <th className="text-left py-3 px-4 text-zinc-300 font-semibold">Type</th>
                  <th className="text-left py-3 px-4 text-zinc-300 font-semibold">Default</th>
                </tr>
              </thead>
              <tbody className="text-zinc-400">
                <tr className="border-b border-zinc-800/50 hover:bg-zinc-800/30">
                  <td className="py-3 px-4 font-mono text-red-400">width</td>
                  <td className="py-3 px-4">number</td>
                  <td className="py-3 px-4 font-mono">64</td>
                </tr>
                <tr className="border-b border-zinc-800/50 hover:bg-zinc-800/30">
                  <td className="py-3 px-4 font-mono text-red-400">height</td>
                  <td className="py-3 px-4">number</td>
                  <td className="py-3 px-4 font-mono">48</td>
                </tr>
                <tr className="border-b border-zinc-800/50 hover:bg-zinc-800/30">
                  <td className="py-3 px-4 font-mono text-red-400">strokeWidth</td>
                  <td className="py-3 px-4">number</td>
                  <td className="py-3 px-4 font-mono">3</td>
                </tr>
                <tr className="border-b border-zinc-800/50 hover:bg-zinc-800/30">
                  <td className="py-3 px-4 font-mono text-red-400">frontStrokeColor</td>
                  <td className="py-3 px-4">string</td>
                  <td className="py-3 px-4 font-mono">#ff4d4f</td>
                </tr>
                <tr className="border-b border-zinc-800/50 hover:bg-zinc-800/30">
                  <td className="py-3 px-4 font-mono text-red-400">animationDuration</td>
                  <td className="py-3 px-4">number</td>
                  <td className="py-3 px-4 font-mono">1.4</td>
                </tr>
                <tr className="border-b border-zinc-800/50 hover:bg-zinc-800/30">
                  <td className="py-3 px-4 font-mono text-red-400">centered</td>
                  <td className="py-3 px-4">boolean</td>
                  <td className="py-3 px-4 font-mono">false</td>
                </tr>
                <tr className="hover:bg-zinc-800/30">
                  <td className="py-3 px-4 font-mono text-red-400">fullscreen</td>
                  <td className="py-3 px-4">boolean</td>
                  <td className="py-3 px-4 font-mono">false</td>
                </tr>
              </tbody>
            </table>
          </div>
        </div>
      </div>
    </div>
  )
}

More by Remco

View all
  • Interactive Empty State

    Interactive Empty State

    345
  • Cube loader

    Cube loader