Hands-on practice is a set of small, self-contained coding projects — each with explicit deliverables and acceptance criteria — that together exercise every layer of the frontend track: HTML/CSS foundations, React UI, state and Effects, Redux Toolkit, TanStack Query, and the Next.js App Router.
In simpler words
You build several different small apps. Each one targets a different slice of frontend skills, so finishing all of them proves you can build a real UI end to end.
These are deliberately NOT the Notes capstone. The Notes UI is your single end-of-week build; this topic is a varied menu of standalone apps (inspired by the roadmap.sh React/Next project lists in the navbar) so you practice the same concepts in fresh contexts.
Build accessible, responsive UI with semantic HTML and CSS layout
Write correct React components with props, state, lists, and events
Use Effects and refs safely for timers and external synchronization
Separate Redux Toolkit client state from TanStack Query server/API data
Host a UI in the Next.js App Router with correct Server/Client boundaries
How to work these projects
Definition
A staged practice set asks you to implement bounded, demoable deliverables, each mapped to specific curriculum concepts, and to prove them in the browser with Network tab and DevTools evidence rather than only a happy-path screenshot.
In simpler words
Finish one project before the next. Each app is standalone — a separate folder or route is fine.
Done means: the feature works, loading/empty/error states are honest, keyboard and focus behaviour is reasonable, and a short README lists how to run it plus which topics it covers.
From the Quiz app onward, Redux Toolkit owns client-only state (drafts, selection, UI chrome) and TanStack Query owns server/API data — never both.
Do not put JWTs in localStorage. Send cookies with Axios withCredentials when a project uses your own backend.
Use free public APIs where noted (Open-Meteo for weather, Open Trivia DB for quiz questions, the GitHub REST API) so you can build without your own backend.
A static, responsive multi-section landing page built with semantic HTML and modern CSS layout — no framework — demonstrating document structure, the box model, Flexbox, CSS Grid, and responsive design.
In simpler words
Build a good-looking, responsive page by hand before adding any React.
Deliverables: semantic landmarks (header/nav, hero, projects grid, about, contact footer); a Flexbox nav and a CSS Grid card layout; a mobile breakpoint with no horizontal scroll; visible focus styles and readable contrast.
Topics covered: HTML structure and semantics, the box model, positioning, Flexbox, CSS Grid, responsive design, basic accessibility.
Prep for React: spot the repeated block (a project card) that becomes a reusable component in Project 2.
Acceptance checklist — Project 1
- [ ] Page uses semantic landmarks (header, nav, main, footer)
- [ ] Nav is a Flexbox row; project cards use CSS Grid
- [ ] Layout reflows cleanly at a mobile breakpoint (no horizontal scroll)
- [ ] Images have alt text; interactive elements have visible focus styles
- [ ] Colour contrast is readable (roughly WCAG AA)
Foundations first — a component library cannot rescue a broken box model.
Project 2 — Flash cards (React fundamentals)
Definition
A flash-card study app built from React components with props, conditional rendering, lists with stable keys, event handlers, and local useState — no Redux or Query yet.
In simpler words
Turn a deck of cards into components. Flip, navigate, and add cards using only local React state.
Deliverables: Card, Deck, and CardForm components; flip to reveal the answer (conditional rendering); next/previous navigation; add/edit/delete cards in local state; a distinct empty-deck view; controlled form inputs.
Topics covered: components, JSX, props, conditional rendering, lists and stable keys, events, controlled inputs, useState, the render/commit mental model.
Prep for state tools: notice the “draft card text” and “current card index” — exactly the client-only values that move into Redux Toolkit next.
Acceptance checklist — Project 2
- [ ] Cards render from an array with stable keys (not index if you reorder/delete)
- [ ] Flip toggles the answer via conditional render, not DOM mutation
- [ ] Handlers are passed, not called during render
- [ ] Empty deck is a distinct state from a deck with cards
- [ ] Draft text + current index are obvious state values (ready to lift into RTK)
UI correctness and handler discipline first; stores arrive next.
A Pomodoro productivity timer that uses useEffect to drive an interval, cleanup to clear it, functional state updates to avoid stale closures, and useRef for values that must persist without triggering re-renders.
In simpler words
Build a work/break countdown. Getting the timer right is the classic place beginners create stale-closure and cleanup bugs.
Deliverables: start/pause/reset controls; automatic switch between work and break intervals; configurable durations; a useEffect interval with proper cleanup; the functional updater form (setSeconds(s => s - 1)); an optional custom useInterval hook; a document title/badge updated via Effect.
Topics covered: useState, useEffect, cleanup functions, dependency arrays, useRef, stale closures, deriving values during render, custom hooks.
This directly reinforces the Week 4 “stale state and Effects” challenge — build it, then re-read that challenge.
Acceptance checklist — Project 3
- [ ] Interval is created in useEffect and cleared in its cleanup
- [ ] Countdown uses the functional updater (no stale closure freezing at one value)
- [ ] Changing the duration does not leak or duplicate intervals
- [ ] Pause/resume keeps the correct remaining time
- [ ] No setInterval left running after unmount
The timer is small on screen but exercises the trickiest Effects rules.
A timed quiz whose questions come from an API via TanStack Query, while the in-progress attempt — current question index, chosen answers, and score draft — lives in Redux Toolkit, proving the course ownership rule.
In simpler words
Questions are server data (Query). Your unfinished attempt is client state (RTK). Never store the question list in Redux.
RTK deliverables (required): configureStore; a quizSlice with currentIndex, answers keyed by questionId, and status/score draft; typed useAppDispatch / useAppSelector; Provider on the client tree.
Query deliverables: useQuery to load questions (Open Trivia DB or a Nest/stub); optional useMutation to submit a result with invalidateQueries; disable submit while pending.
Auth (only if you use your own backend): Axios withCredentials; 401 → prompt login; no JWT in localStorage.
Polish: honest loading/empty/error; a results screen derived from RTK answers + Query questions, not a third stored copy.
Topics covered: RTK store/slice/hooks, client vs server ownership, TanStack Query, Axios/cookies, deriving UI from two sources.
Acceptance checklist — Project 4
- [ ] configureStore + quizSlice + typed hooks committed
- [ ] Questions live only in Query (never copied into Redux)
- [ ] Selected answers + current index live in RTK
- [ ] Score is derived from RTK answers, not stored twice
- [ ] Loading/empty/error are distinct; submit disabled while pending
- [ ] No JWT in localStorage
If the question list is also in Redux, this project fails the ownership bar.
Keep the attempt client-owned. Do not store the fetched questions[] here.
Project 5 — Weather app (Next.js App Router)
Definition
A weather lookup app hosted in the Next.js App Router, with a Server Component page shell composing a Client Component search leaf, next/link navigation, route metadata, and TanStack Query fetching a public weather API from the client subtree.
In simpler words
Build the shell on the server; keep the interactive search on the client. Data still comes through Query.
Deliverables: an app route (e.g. /weather); a Server Component page composing a client <WeatherSearch>; AppProviders (QueryClientProvider, plus a Redux Provider if you carry a search draft) wrapping only the client subtree; next/link nav; loading/error UI; page metadata; city search or geolocation.
Data: use a free API such as Open-Meteo (no key required) via Query; include the resolved location in the query key.
Topics covered: Next layouts/pages, Link, Server vs Client Components, Provider placement, metadata, fetching with Query inside a Client Component.
Acceptance checklist — Project 5
- [ ] use client only on the interactive leaf + providers module
- [ ] QueryClientProvider is not on a Server Component file
- [ ] Internal navigation uses next/link
- [ ] Query key includes the searched location; empty ≠ loading ≠ error
- [ ] Page sets metadata (title/description)
Moving into Next must not turn the whole route into a client component for one input.
A GitHub repository explorer where filter inputs (query text, language, sort) are held as a filterDraft in Redux Toolkit and copied to appliedFilters only on submit, while TanStack Query owns the results using the applied filters and page in its query key.
In simpler words
Typing in filters updates RTK only. Pressing Search commits the filters and Query refetches — no request spam on every keystroke.
RTK deliverables: a filtersSlice with filterDraft (q/language/sort), appliedFilters (committed on submit), and UI chrome flags (e.g. isDetailsOpen); actions applyFilters / resetFilters / toggleDetails.
Query deliverables: queryKey includes appliedFilters + page; results refetch when applied filters change — not on every keystroke; pagination via the GitHub Search API.
Proof: an ARCHITECTURE.md ownership table (React / RTK / Query / Next / API) with one sentence each, and a demo script: type filters → apply → paginate → open a repo.
Topics covered: RTK for shared client UI chrome, Query keys and pagination, the revision map, the Query-vs-RTK challenge, failure honesty.
Acceptance checklist — Project 6
- [ ] Typing in filters updates RTK filterDraft only (Network tab shows no request per keystroke)
- [ ] Submit copies filterDraft → appliedFilters and the Query key changes
- [ ] Repo results live only in Query; never mirrored into Redux
- [ ] Pagination is part of the query key; empty/loading/error are distinct
- [ ] ARCHITECTURE.md names RTK for chrome/drafts and Query for API data
RTK handles chrome and drafts; Query handles reads. This is the whole course thesis on one screen.
Ownership cheat sheet
RTK: filterDraft, appliedFilters, selected id, UI flags
Query: repo results + pagination (GitHub API)
Next: routes, RSC shell, metadata
API: the external service is the source of truth
If a value could change on the server for another user, it does not belong in RTK.
Suggested order and coverage map
Definition
A coverage map lists which curriculum concepts each project exercises, so the set as a whole leaves no major frontend skill untested.
In simpler words
Do them in order; together they cover the entire frontend track.