Project 7

Project 7 · Capstone
Dashboard Capstone — React Query, Next.js, Charts
Pulling together routing, server data, and visualization into one real-feeling application
Difficulty: Capstone Introduces: React Query, Next.js, Recharts
This is the final project in the series — the first one to combine a server-data library, a different framework (Next.js instead of plain Vite), and a charting library, all in one build. Three brand-new tools are introduced below, each with its own primer; none of them have a full course chapter yet, since React Query and Next.js are both covered properly in Advanced Chapters 2 and 7.
Skills This Project Exercises
React Query (useQuery) Next.js file-based routing Dynamic routes Charting (Recharts) Loading/error states, simplified
Next.js — the minimum you need for this project
Scaffold with npx create-next-app@latest. The headline difference from Vite: pages are defined by the file system rather than by code you write yourself — a file at app/page.js becomes the / route, and a folder named app/coin/[id]/page.js becomes a dynamic route matching /coin/anything, with anything readable inside that page via Next's routing hooks. Everything learned about components, props, and state still applies unchanged — Next.js only changes how a component becomes a "page."
React Query — the minimum you need for this project
Install with npm install @tanstack/react-query, and wrap the app once in a <QueryClientProvider>. From there, useQuery replaces the entire "useState + useEffect + manual loading/error flags" pattern from earlier projects in one hook call:
const { data, isLoading, isError } = useQuery({ queryKey: ["coins"], queryFn: () => fetch("https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd").then((r) => r.json()), });
React Query handles the loading/error state, caches the result under queryKey, and can automatically refetch it later — all without a single useEffect written by hand.

Suggested Data Source

The CoinGecko API is free and needs no API key for its public market-data endpoints — a good fit for a dashboard with multiple live-updating widgets and a price history chart per item.

Requirements

  • An overview page listing several items (e.g. top cryptocurrencies) with at least name, current price, and 24h change, fetched with useQuery.
  • Clicking an item navigates (via Next.js routing) to a detail page for that specific item, at a dynamic URL.
  • The detail page fetches and renders a price-history chart using Recharts (a line chart is enough).
  • Loading and error states on both pages are handled entirely through React Query's isLoading/isError flags — no manually-managed loading state alongside it.
  • The overview page's data refreshes automatically after some interval (React Query's refetchInterval option) without a manual page reload.
  • The layout reads reasonably on both a desktop-width and a narrow/mobile-width viewport.

Suggested Component Breakdown

app/layout.js // wraps everything in QueryClientProvider app/page.js // route "/" — DashboardPage └── DashboardPage └── CoinList // useQuery — list of coins └── CoinCard (×N) // Link to /coin/[id] app/coin/[id]/page.js // route "/coin/:id" — CoinDetailPage └── CoinDetailPage └── PriceChart // useQuery — price history; Recharts LineChart

A Reasonable Build Order

  1. Scaffold the Next.js app and confirm the default starter page runs, before changing anything.
  2. Install React Query, set up QueryClientProvider in the root layout, and get one useQuery call successfully logging fetched data to the console.
  3. Build CoinList/CoinCard, rendering real data from that query, each card linking to its own dynamic detail route.
  4. Build the dynamic route page, reading the id from the URL and firing a second useQuery for that specific item's history data.
  5. Install Recharts and get a <LineChart> rendering the fetched price-history data on the detail page.
  6. Add refetchInterval to the overview query last, and confirm (via the network tab) that it's actually refetching on its own over time.
Don't re-introduce manual loading state on top of React Query
A common mistake when first adopting React Query is keeping an old habit alive — adding a separate useState for loading/error on top of the isLoading/isError React Query already provides. That duplicates logic React Query is specifically meant to remove; lean entirely on the flags useQuery returns instead.
Stretch Goals
  • Add a Next.js API route as a thin proxy to the external API, avoiding any rate-limit/CORS concerns on the client.
  • Add a search/filter box on the overview page.
  • Add a second chart type (e.g. a bar chart comparing 24h change across items).
  • Add a dark/light theme toggle, persisted across visits.
  • Deploy the finished app to Vercel — Next.js's own hosting platform, built for exactly this kind of project.
This project has no single solution file — the requirements above and your own judgment are the spec. This also wraps up the React Projects course: seven briefs, beginner through capstone, covering everything from a first todo list to a real multi-tool dashboard.