Components
Loading preview...
Create a stunning, retro-style animated counter for your React applications with the FlipCountdown component. This component mimics the classic split-flap display of old-school clocks and airport departure boards, providing a visually engaging way to display numbers. Built for ultimate flexibility, FlipCountdown can handle both counting down and counting up between any two numbers, no matter how large, thanks to its use of BigInt. It's perfect for launch pages, event timers, displaying real-time statistics, or adding a dynamic flair to any dashboard. The component is fully responsive and allows for easy customization of the card and text colors to perfectly match your theme. Simply set the countFrom and countTo props and watch the numbers flip!
@easemize
npx shadcn@latest add https://21st.dev/r/easemize/flip-countdownimport React, { useState } from 'react';
import { FlipCountdown } from "@/components/ui/flip-countdown";
export default function FlipCountdownDemo() {
const [key, setKey] = useState(0);
// Use strings for state to accommodate large numbers from input fields
const [countFrom, setCountFrom] = useState('100');
const [countTo, setCountTo] = useState('0');
const restartCountdown = () => {
// A non-empty value is required for the component to render
if (countFrom.trim() === '' || countTo.trim() === '') {
alert("Please enter a 'From' and 'To' value.");
return;
}
setKey(prevKey => prevKey + 1);
};
// Restrict input to only digits
const handleInputChange = (
e: React.ChangeEvent<HTMLInputElement>,
setter: React.Dispatch<React.SetStateAction<string>>
) => {
const value = e.target.value.replace(/[^0-9]/g, '');
setter(value);
}
return (
<div className="flex flex-col items-center gap-8 p-4">
{/* The component now accepts string props for large numbers */}
<FlipCountdown
key={key}
countFrom={countFrom}
countTo={countTo}
/>
<div className="flex flex-wrap items-center justify-center gap-4">
<div className="flex flex-col gap-1.5">
<label htmlFor="from" className="text-sm font-medium text-muted-foreground">From</label>
<input
id="from"
type="text" // Use text to allow for very large numbers
inputMode="numeric" // Helps mobile users get a number pad
value={countFrom}
onChange={(e) => handleInputChange(e, setCountFrom)}
className="w-32 rounded-md border bg-transparent p-2 text-center"
placeholder="e.g., 10000"
/>
</div>
<div className="flex flex-col gap-1.5">
<label htmlFor="to" className="text-sm font-medium text-muted-foreground">To</label>
<input
id="to"
type="text"
inputMode="numeric"
value={countTo}
onChange={(e) => handleInputChange(e, setCountTo)}
className="w-32 rounded-md border bg-transparent p-2 text-center"
placeholder="e.g., 0"
/>
</div>
<button
onClick={restartCountdown}
className="self-end rounded-md bg-primary px-4 py-2 text-primary-foreground transition-colors hover:bg-primary/90"
>
Restart
</button>
</div>
</div>
);
}