Skip to main content

Overview

The FavoriteButton component provides a toggle button for adding and removing Pokemon from a user’s favorites list. It integrates with Zustand for state management and persists favorites to local storage.

Import

import { FavoriteButton } from '@/components/pokemon'

Props

pokemon
PokemonSummary
required
Pokemon data object to be added/removed from favorites
interface PokemonSummary {
  id: number
  name: string
  types: PokeType['name'][]
  image: string
}

Usage Example

import { FavoriteButton } from '@/components/pokemon'
import { PokemonSummary } from '@/types'

const pokemon: PokemonSummary = {
  id: 25,
  name: 'pikachu',
  types: ['electric'],
  image: 'https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/25.png'
}

export default function Example() {
  return (
    <div>
      <h1>Pikachu</h1>
      <FavoriteButton pokemon={pokemon} />
    </div>
  )
}

Features

State Management

Uses Zustand store for favorites management:
const { toggleFavorite } = useFavoriteActions()
const isFavorite = useIsFavorite(id)

Dynamic Button State

The button changes appearance and text based on favorite status: When NOT favorited:
  • Default button styling
  • Heart icon
  • “Add to favorites” text
When favorited:
  • White background with black text
  • Close/X icon
  • “Remove from favorites” text
  • Bold font weight

Responsive Width

Adapts to screen size:
  • Full width on mobile (w-full)
  • Auto width on desktop (md:w-fit)

Component Structure

export const FavoriteButton = ({ pokemon }: Props) => {
  const { id } = pokemon
  const { toggleFavorite } = useFavoriteActions()
  const handleToggleFavorite = () => toggleFavorite(pokemon)
  const isFavorite = useIsFavorite(id)

  return (
    <Button
      className={`flex items-center justify-center gap-3 w-full md:w-fit ${
        isFavorite ? 'bg-white! text-black! font-semibold!' : ''
      }`}
      onClick={handleToggleFavorite}
    >
      {isFavorite ? (
        <>
          <CgClose className="text-xl" />
          <span>Remove from favorites</span>
        </>
      ) : (
        <>
          <IoHeart className="text-2xl" />
          <span>Add to favorites</span>
        </>
      )}
    </Button>
  )
}

Zustand Store Integration

useFavoriteActions

Provides action methods:
const { toggleFavorite } = useFavoriteActions()

// Toggle a Pokemon's favorite status
toggleFavorite(pokemon: PokemonSummary)

useIsFavorite

Returns the favorite status of a specific Pokemon:
const isFavorite = useIsFavorite(pokemonId: number)
// Returns: boolean

Persistence

Favorites are automatically persisted to local storage through the Zustand store middleware. Changes are immediately synchronized across all instances of the component.

Icons

Uses React Icons library:
  • IoHeart - Solid heart icon (from react-icons/io5)
  • CgClose - Close/X icon (from react-icons/cg)

Styling Details

Base Styles

className="flex items-center justify-center gap-3 w-full md:w-fit"

Favorite State Styles

isFavorite ? 'bg-white! text-black! font-semibold!' : ''

Favorite State Styles

isFavorite ? 'bg-white! text-black! font-semibold!' : ''
The ! suffix in class names (e.g., bg-white!) is UnoCSS syntax for !important, ensuring these styles override any conflicting styles.

Icon Sizes

  • Heart icon: text-2xl
  • Close icon: text-xl

Accessibility

The button provides clear visual and textual feedback:
  • Descriptive text labels (“Add to favorites” / “Remove from favorites”)
  • Icon reinforces the action
  • High contrast when favorited (white background, black text)

Store Hooks

useFavoriteActions

Location: @/stores/favorite.store Provides methods:
  • toggleFavorite(pokemon: PokemonSummary) - Add or remove from favorites
  • addFavorite(pokemon: PokemonSummary) - Add to favorites
  • removeFavorite(id: number) - Remove from favorites
  • clearFavorites() - Remove all favorites

useIsFavorite

Location: @/stores/favorite.store Signature:
useIsFavorite(id: number): boolean
Returns true if Pokemon with given ID is in favorites, false otherwise.

Example: Building a Favorites Page

import { FavoriteButton } from '@/components/pokemon'
import { useFavoriteStore } from '@/stores/favorite.store'

export default function FavoritesPage() {
  const favorites = useFavoriteStore((state) => state.favorites)
  
  return (
    <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
      {favorites.map((pokemon) => (
        <div key={pokemon.id} className="border rounded p-4">
          <h3>{pokemon.name}</h3>
          <img src={pokemon.image} alt={pokemon.name} />
          <FavoriteButton pokemon={pokemon} />
        </div>
      ))}
    </div>
  )
}

Source Location

/src/components/pokemon/FavoriteButton.tsx:1-38

Build docs developers (and LLMs) love