Skip to main content
The app has two pieces of state that need to be accessible from anywhere in the component tree:
  • Favorites — the list of crypto IDs the user has saved
  • Color mode — whether the UI is in dark or light mode
Both are implemented with React context in src/context/useContext.jsx. Each context is created, provided, and consumed through a dedicated hook, keeping component code clean.

FavoritesProvider and useFavorites

FavoritesProvider delegates its state logic entirely to the useFavorites hook from src/hooks/useFavorites.jsx. The hook manages an array of saved coin IDs, persists it to localStorage, and exposes four values to consumers.
// src/hooks/useFavorites.jsx
export function useFavorites() {
  const [favorites, setFavorites] = useState(() => {
    try {
      const savedFavorites = localStorage.getItem("cryptoFavorites");
      return savedFavorites ? JSON.parse(savedFavorites) : [];
    } catch {
      return [];
    }
  });

  useEffect(() => {
    localStorage.setItem("cryptoFavorites", JSON.stringify(favorites));
  }, [favorites]);

  const addFavorite = (cryptoId) => {
    setFavorites((prev) => [...prev, cryptoId]);
  };

  const removeFavorite = (cryptoId) => {
    setFavorites((prev) => prev.filter((id) => id !== cryptoId));
  };

  const isFavorite = (cryptoId) => {
    return favorites.includes(cryptoId);
  };

  return { favorites, addFavorite, removeFavorite, isFavorite };
}
The initial state is loaded lazily from localStorage so favorites survive page refreshes without a separate effect on mount.

Context values exposed by useFavorites

ValueTypeDescription
favoritesstring[]Array of saved coin IDs (e.g. ["bitcoin", "ethereum"])
addFavorite(cryptoId)functionAppends a coin ID to the favorites list
removeFavorite(cryptoId)functionRemoves a coin ID from the favorites list
isFavorite(cryptoId)functionReturns true if the coin ID is in the list
FavoritesProvider passes the entire object returned by useFavorites as the context value:
// src/context/useContext.jsx
const FavoritesContext = createContext();

export const FavoritesProvider = ({ children }) => {
  const favoritesData = useFavoritesHook();

  return (
    <FavoritesContext.Provider value={favoritesData}>
      {children}
    </FavoritesContext.Provider>
  );
};

export const useFavorites = () => {
  return useContext(FavoritesContext);
};
Any component that calls useFavorites() gets the full { favorites, addFavorite, removeFavorite, isFavorite } object.

ColorModeProvider and useColorMode

ColorModeProvider manages a single boolean darkMode and a toggle function. It also persists the preference to localStorage and applies or removes the dark class on <html> so Tailwind’s dark-mode utilities take effect.
// src/context/useContext.jsx
const ColorModeContext = createContext();

export const ColorModeProvider = ({ children }) => {
  const [darkMode, setDarkMode] = useState(false);

  function changeColorMode() {
    setDarkMode(!darkMode);
  }

  // Restore saved preference on first mount
  useEffect(() => {
    const saved = localStorage.getItem("saveMode");
    if (saved === "true") {
      setDarkMode(true);
    }
  }, []);

  // Apply class and persist on every change
  useEffect(() => {
    localStorage.setItem("saveMode", darkMode);
    if (darkMode) {
      document.documentElement.classList.add("dark");
    } else {
      document.documentElement.classList.remove("dark");
    }
  }, [darkMode]);

  return (
    <ColorModeContext.Provider value={{ darkMode, changeColorMode }}>
      {children}
    </ColorModeContext.Provider>
  );
};

export const useColorMode = () => {
  return useContext(ColorModeContext);
};

How dark mode works

1

Preference restored on mount

The first useEffect (empty dependency array) reads localStorage.getItem("saveMode"). If it equals the string "true", darkMode is set to true.
2

Class toggled on change

The second useEffect runs whenever darkMode changes. It adds the dark class to document.documentElement (<html>) when enabled and removes it when disabled.
3

Preference persisted

The same effect writes the current value back to localStorage under the key saveMode, so the choice survives a page reload.
4

Components react via hook

Any component can call useColorMode() to read darkMode or call changeColorMode() to toggle it. Tailwind dark: variants on those components update automatically.

Context values exposed by useColorMode

ValueTypeDescription
darkModebooleantrue when dark mode is active
changeColorModefunctionToggles darkMode between true and false

Provider nesting in App.jsx

Both providers wrap the entire component tree inside App, so every route and every component can access either context without prop drilling.
// src/App.jsx
function App() {
  return (
    <>
      <FavoritesProvider>
        <ColorModeProvider>
          <Navbar />

          <Routes>
            <Route path="/" element={<Home />} />
            <Route path="/grafics" element={<Grafics />} />
            <Route path="/favs" element={<Favorites />} />
            <Route path="/news" element={<News />} />
            <Route path="/converter" element={<ConverterPage />} />
          </Routes>
        </ColorModeProvider>
      </FavoritesProvider>
    </>
  );
}
FavoritesProvider is the outer wrapper because it has no dependency on color mode. ColorModeProvider is nested inside it. Navbar and all page components are children of both, meaning a single call to useFavorites() or useColorMode() inside any of them resolves to the correct context value.
Because both hooks call useContext internally, components only re-render when the specific context value they consume changes. A color mode toggle does not re-render components that only call useFavorites(), and vice versa.

Build docs developers (and LLMs) love