react-flip-cards
v1.0.8
Published
Tiny (~2kB), ref-driven 3D flip-card component for React. One flexible primitive for flip clocks, countdowns, counters, tickers and scoreboards.
Downloads
1,107
Maintainers
Readme
react-flip-cards
Flip cards for React — a little toy for when you need digits that flip, roll, or spin.

⭐ Star this repository if you’d like to support its growth
▶ Live demo & copy-paste examples →
The story
I wanted flip cards and found @leenguyen/react-flip-clock-countdown — a great component, but I needed something more flexible.
This is the light unopinionated version, perfect for any type of digit animation.
Install
npm install react-flip-cards
# or: bun add / pnpm add / yarn add react-flip-cardsUsage
You own the values. Render a panel and push numbers into it — declaratively via props, or imperatively through a ref:
import { useEffect, useRef } from 'react';
import FlipCardPanel, { FlipCardRef } from 'react-flip-cards';
import 'react-flip-cards/styles.css';
const pad = (n: number) => String(n).padStart(2, '0').split('').map(Number);
export function Clock() {
const ref = useRef<FlipCardRef>(null);
useEffect(() => {
const tick = () => {
const d = new Date();
ref.current?.set([...pad(d.getHours()), ...pad(d.getMinutes()), ...pad(d.getSeconds())]);
};
tick();
const id = setInterval(tick, 1000);
return () => clearInterval(id);
}, []);
// separators={[1, 3]} → colons after the 2nd and 4th card → HH:MM:SS
return <FlipCardPanel ref={ref} nrCards={6} separators={[1, 3]} />;
}Scoreboards, countdowns, odometers, combination locks — they're all the same component with different values. See the live demo for those, each with copyable source.
Ref API
FlipCardPanel forwards a ref exposing:
| Method | Description |
| -------------------------- | ---------------------------------------------------------------- |
| set(values: number[]) | Set every card at once; changed cards flip. |
| set(index, value) | Set a single card, e.g. set(1, 7). |
| set(updater) | Functional update, e.g. set((prev) => prev.map((v) => v + 1)). |
| increment(index: number) | Increment one card (wraps 9 → 0). |
| reset() | Reset all cards to 0. |
| getValue(): number[] | Read the currently displayed values. |
Props
FlipCardPanel accepts all div props plus:
| Name | Type | Default | Description |
| ---------------- | ----------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| nrCards | number | — | Required. Number of flip cards to render. |
| initialValue | number[] | all 0 | Initial value (0–9) per card; read once at mount. |
| onChange | (values: number[]) => void | — | Fires whenever displayed values change — track state without mirroring it. |
| labels | (string \| ReactElement)[] | — | Label under each card. |
| showLabels | boolean | true | Toggle label visibility. |
| separators | number[] | — | Show colons after these card indices, e.g. [1, 3] → HH:MM:SS. |
| showSeparators | boolean | false | Show a colon between every card. |
| separatorStyle | { color?, size? } | — | Separator styling. |
| blockStyle | CSSProperties | — | Card styles: width, height, fontSize, color, background, borderRadius, boxShadow. |
| labelStyle | CSSProperties | — | Label styles (fontSize, color, …). |
| showDivider | boolean | true | Show the horizontal divider across each card. |
| dividerStyle | { color?, height? } | — | Divider styling. |
| duration | number | 0.7 | Flip animation duration (seconds). |
| mode | 'sync' \| 'queue' \| 'spin' | sync | How cards animate to new values. sync flips straight to the latest (never drops an update); queue rolls through every intermediate digit at flip speed (can lag a far target); spin scrolls an odometer to the latest value in one duration (always lands on time). queue/spin are numeric-only. |
| faces | (number \| string)[] | — | Custom face content, indexed by value — e.g. ['Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su'] for a weekday card. set/increment still use numeric indices (increment wraps at faces.length). Content can vary in width; set blockStyle.width to fix it. queue/spin stay numeric-only and ignore faces. |
| spacing | number \| string | — | Gap between cards / separators. |
Theming
Everything is a CSS custom property (prefixed --fcp-). Override globally in your CSS, or per-instance via blockStyle / labelStyle / separatorStyle / dividerStyle.
.fcp__container {
--fcp-background: #0f181a;
--fcp-digit-color: #fff;
--fcp-digit-block-radius: 4px;
--fcp-flip-duration: 0.7s;
}Extending
It's a small, value-driven primitive, so most "features" are just how you drive it:
- Animation feel — pick a
mode(sync/queue/spin) andduration.FlipCard(3D flip) andOdometerCard(vertical scroll) are both exported if you want to use a single card directly. - Looks — it's all
--fcp-*CSS variables; restyle without touching the component. - Behaviour — there are no internal timers or data fetching by design. You hold the values and
set()them, so a clock, a counter, or a live feed are all the same three lines.
