Start with the mental model, not the toolchain
When we teach React at Toolliyo, we see the same mistake: beginners install five libraries before they understand what a component is. React is a library for building user interfaces by composing functions that return UI. That is the whole game. Everything else—routing, data fetching, styling—layers on top once you can predict when your screen re-renders and why.
You will use JavaScript first, then TypeScript. TypeScript is not optional on most production teams in 2025, but learn React patterns in JS so you are not fighting two learning curves on day one.
Phase 1: JSX, components, and props
Your first components
Build a small product list: a parent passes an array of items; child components render each row. Practice lifting state only when siblings need to share it.
function ProductRow({ name, price, onAdd }) {
return (
<li>
<span>{name}</span>
<button onClick={onAdd}>Add — {price{'}'}</button>
</li>
);
}
Rules that stick: one component per file once projects grow, PascalCase names, never mutate props, keys on lists must be stable IDs—not array indexes when items reorder.
Conditional rendering and composition
Prefer composition over prop drilling early. Use children, small wrapper components, and early returns for loading and error states instead of nested ternaries that become unreadable.
Phase 2: State, effects, and hooks
useState and useReducer
useState handles most local UI state. Reach for useReducer when transitions have rules: wizard steps, form state with interdependent fields, or cart logic where the next state depends on an action type.
useEffect without the foot-guns
Effects synchronize React with external systems: subscriptions, document title, syncing to localStorage. They are not "run after render for anything." Before adding an effect, ask: can this be computed during render? Can an event handler do this instead?
- Always specify dependency arrays honestly—stale closures cause real bugs.
- Return cleanup functions for subscriptions and timers.
- Do not fetch data in effects without a strategy for race conditions; consider React Query or similar once you understand the underlying problem.
Custom hooks
Extract repeated logic into hooks like useDebouncedValue or useLocalStorage. This is how you share behavior without inheritance. Interviewers and code reviewers both look for this skill.
Phase 3: Routing, forms, and data
React Router
Learn nested routes, loaders where your stack supports them, and protected routes for authenticated areas. Keep route definitions close to feature folders in larger apps.
Forms
Controlled inputs teach you state flow. For complex forms, use React Hook Form or Formik—but build one form by hand first so validation and error display make sense.
Fetching data
Start with fetch and explicit loading/error UI. Then adopt TanStack Query (React Query) for caching, retries, and background refetch. Understand server state vs client state; not everything belongs in Redux.
Phase 4: TypeScript and component contracts
Define props with interfaces, use discriminated unions for variant components, and type event handlers. Avoid any; use unknown at boundaries and narrow. Generic components (List<T>) show maturity.
Phase 5: Styling and design systems
Pick one approach and go deep: CSS Modules, Tailwind, or a component library like MUI or Radix plus your tokens. Production apps need consistent spacing, focus states, and responsive layouts—not one-off inline styles on every element.
Phase 6: Testing and quality
- Unit tests — Vitest or Jest for pure functions and hooks with Testing Library.
- Component tests — test behavior users see, not implementation details.
- E2E — Playwright for critical flows: login, checkout, core CRUD.
Add ESLint, Prettier, and CI that runs tests on pull requests before you call the project "production-ready."
Phase 7: Performance and deployment
Learn when useMemo and useCallback help (often less than you think) and when code splitting with React.lazy matters. Measure with React DevTools Profiler before optimizing.
Deploy to Vercel, Netlify, or Azure Static Web Apps. Configure environment variables for API URLs, set up preview deployments per branch, and add error monitoring with Sentry or equivalent.
Portfolio project sequence
- Todo app with filters and persistence—hooks and state.
- Dashboard consuming a public API—loading, errors, pagination.
- Multi-page app with auth—routing, protected routes, token refresh.
- Same app with tests and CI—this is what hiring managers want to see.
Production-ready means the next developer can change a feature without fear. Comment less, structure more: feature folders, clear naming, typed boundaries, and tests on business-critical paths. That is the bar we set for React developers leaving Toolliyo programs—and it should be yours too.