GameCard component displays a game’s cover art with hover effects, favorite toggles, and real-time artwork sync feedback.
Import
Basic usage
Props
Game object to display. See the Game interface below for details.
Called when the user clicks the card to play the game. Receives the game object and optional card bounding rect for animations.
Deprecated: Use
menuItems instead. Called when the user clicks the options button.Menu items shown in the options dropdown on hover.
Factory function that returns menu items. Called lazily when dropdown opens. Preferred over
menuItems for memoization.Called when the user toggles the favorite heart on this card.
External store for artwork sync phases. The card subscribes to its own game’s phase.
Whether this game is currently being launched. Shows a shimmer overlay and wait cursor.
Whether this card is disabled (e.g., another game is launching). Dims the card and prevents interaction.
Additional CSS classes to apply to the card.
Inline styles forwarded to the root card element (useful for animation delays).
Ref forwarded to the root Card element (used for FLIP measurements).
Game interface
packages/ui/components/GameCard.tsx
Examples
With favorite toggle
With menu items
With lazy menu items (recommended)
With artwork sync
Artwork sync phases
The card displays different states during artwork sync:hashing- TV static with pulsing animationquerying- TV static while querying ScreenScraperdownloading- TV static while downloading imagedone- Cross-fade from static to cover arterror- Brief red flash then return to fallbacknot-found- Static with “Artwork not found” label
The
done phase auto-clears after 1.5s. The error phase auto-clears after 2.5s. The not-found phase persists so users know not to retry.Visual states
Hover effects
- Card scales to 105% with shadow
- Favorite heart appears (if
onToggleFavoriteprovided) - Options button appears (if
menuItemsoronOptionsprovided)
Launch state
- Shimmer overlay animation
- Scale to 105% and elevated shadow
- Wait cursor
Disabled state
- Opacity reduced to 50%
- Pointer events disabled
- No hover effects
Accessibility
- Card is keyboard focusable with
tabIndex={0} - Supports Enter and Space keys to play
- ARIA label:
"Play {game.title}" - Favorite button has descriptive ARIA label
- Options button has descriptive ARIA label
Performance notes
- Uses
React.memowith custom comparator to skip re-renders - Menu items are generated lazily when dropdown opens
- Artwork sync uses external store with
useSyncExternalStoreto bypass React - Cover art image always in DOM to prevent mount/unmount flicker