This capstone consolidates the entire course into one production-grade React 19 application, built and shipped to the standard real products demand. It brings together everything: a component architecture with hooks, React 19 features (Actions, Suspense, optimistic updates), routing, server-state data fetching, forms, client-state management, performance optimisation, testing, TypeScript, styling, animation, error handling, accessibility, deployment, monitoring, and security — integrated into a coherent, deployed, observable, secure app.
You will build a complete application — for example a task-management or content app — that is type-safe, well-tested, performant, accessible, animated where it helps, resilient to errors, deployed to a platform with monitoring, and secure by default. The emphasis is on integrating the full toolkit into something that meets a professional bar end to end, not on any single feature in isolation.
Learning Objectives
- Integrate the full course: components/hooks, React 19 features, routing, data fetching, state, forms, performance, testing, TypeScript, styling, animation, error handling, accessibility, deployment, monitoring, and security.
- Build a type-safe app in TypeScript with a clean component architecture and reusable, tested components.
- Manage server state with a data-fetching library and client state appropriately (Zustand/context/local), with React 19 features where they fit.
- Make it performant (profiled, memoised, code-split), accessible (semantic, keyboard, ARIA), and resilient (error boundaries).
- Deploy to a platform (Vercel/Netlify) with correct SPA routing, integrate error tracking and performance monitoring, and handle data securely.
- Cover key behaviour with Vitest + React Testing Library tests.
Technical Requirements
- TypeScript throughout, with typed props, hooks, and a typed data layer; a clean component/hook structure.
- Multi-page routing (React Router) with nested layouts, dynamic routes, and a 404; server state via TanStack Query with loading/error/empty states and mutations + invalidation.
- Forms with validation (React Hook Form or React 19 Actions), appropriate client state (Zustand/context/local), and React 19 features (Suspense, useOptimistic/Actions) where they add value.
- Performance (route-based code splitting, profiled memoisation), accessibility (semantic markup, labelled forms, focus management, keyboard support), and error boundaries with recoverable fallbacks.
- A consistent styling approach (Tailwind/CSS Modules), purposeful accessible animation (Framer Motion respecting reduced-motion).
- Deployment to Vercel/Netlify with SPA fallback and no client secrets, error tracking + Web Vitals monitoring, secure data handling (escaping, sanitisation, server-trust boundary), and a suite of Vitest/RTL tests.
Architecture & Design
The app is a TypeScript React 19 application structured into pages (routed via React Router with nested layouts), reusable UI components, and custom hooks. Server state lives in TanStack Query (cached reads, mutations with invalidation), client state in Zustand or context as appropriate, and React 19 features (Suspense for reads, Actions + useOptimistic for writes) handle asynchronicity declaratively. Forms use React Hook Form with validation, styling is consistent (Tailwind or CSS Modules), and Framer Motion adds purposeful, reduced-motion-aware animation.
Cross-cutting concerns are woven throughout: error boundaries wrap routes and key widgets for resilience, accessibility is built in (semantic structure, labelled and error-associated forms, focus management, keyboard support), performance is addressed with route-based code splitting and profiled memoisation, and security is secure-by-default (JSX escaping, sanitised HTML, no client secrets, server-trusted validation). The app is deployed to Vercel/Netlify with the SPA fallback, instrumented with error tracking and Web Vitals, and covered by Vitest/RTL tests — a complete production picture.
// Production architecture: every concern in its place (TypeScript throughout)
// main.tsx
import * as Sentry from "@sentry/react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { createBrowserRouter, RouterProvider } from "react-router-dom";
import { ErrorBoundary } from "react-error-boundary";
Sentry.init({ dsn: import.meta.env.VITE_SENTRY_DSN }); // monitoring
const queryClient = new QueryClient(); // server-state cache
const router = createBrowserRouter([/* nested layouts, dynamic routes, 404, lazy pages */]);
createRoot(document.getElementById("root")!).render(
<ErrorBoundary FallbackComponent={AppError} // resilience (top-level net)
onError={(e, i) => Sentry.captureException(e, { extra: i })}>
<QueryClientProvider client={queryClient}>
<RouterProvider router={router} /> {/* routing */}
</QueryClientProvider>
</ErrorBoundary>
);Phase 1 — Typed Foundation, Routing, and Data Layer
Scaffold a TypeScript React 19 + Vite project and build the foundation: React Router with nested layouts, dynamic routes, and a 404; a typed data layer using TanStack Query for server state with proper loading, error, and empty states; and route-based code splitting so each page loads on demand. Establish reusable UI components and custom hooks with full typing.
Get the skeleton working end to end — navigation, cached data display, lazy-loaded routes — before layering features. This phase sets the type-safe, well-structured base: the query cache as the single source of truth for server data, routes mirroring the app's structure, and code splitting keeping the initial load light.
// Typed data hook + lazy routes (foundation)
import { useQuery } from "@tanstack/react-query";
interface Task { id: string; title: string; done: boolean }
export const useTasks = () =>
useQuery<Task[]>({ queryKey: ["tasks"], queryFn: () => api.getTasks(), staleTime: 30_000 });
const TaskBoard = lazy(() => import("./pages/TaskBoard")); // route-based code split
// router: { path: "/", element: <Layout/>, children: [
// { index: true, element: <Suspense fallback={<Spinner/>}><TaskBoard/></Suspense> },
// { path: "tasks/:id", element: <TaskDetail/> }, { path: "*", element: <NotFound/> } ]}Phase 2 — Features: Forms, State, React 19, Styling, Animation
Build the core features: validated forms (React Hook Form or React 19 Actions) for creating and editing, with mutations via TanStack Query that invalidate affected queries. Use Suspense for reads and useOptimistic/Actions for snappy writes where they add value, manage client state with Zustand or context as appropriate, and apply a consistent styling approach (Tailwind or CSS Modules).
Add purposeful, accessible animation with Framer Motion (respecting reduced-motion) for transitions and feedback, keeping it subtle. Throughout, build accessibility in — semantic markup, labelled and error-associated inputs, focus management on navigation and in modals, full keyboard operability — rather than retrofitting it. This phase makes the app fully featured and polished.
// Mutation + optimistic write + invalidation (React 19 + TanStack Query)
function useAddTask() {
const qc = useQueryClient();
return useMutation({
mutationFn: (title: string) => api.addTask(title),
onMutate: async (title) => { /* optimistic cache update */ },
onSettled: () => qc.invalidateQueries({ queryKey: ["tasks"] }),
});
}
// Accessible, animated, validated form (RHF + Framer Motion + a11y)
// <form onSubmit={handleSubmit(onSubmit)}>
// <label htmlFor="title">Title</label>
// <input id="title" aria-invalid={!!errors.title} {...register("title",{required:true})} />
// <motion.button whileTap={{ scale: 0.97 }} disabled={isSubmitting}>Add</motion.button>Phase 3 — Performance, Resilience, Deployment, Monitoring, Security, Tests
Harden the app for production: profile with the React DevTools Profiler and apply targeted memoisation, confirm route-based code splitting, wrap routes and key widgets in error boundaries with recoverable fallbacks, and secure data handling (rely on JSX escaping, sanitise any injected HTML, keep secrets server-side, validate on the server). Write Vitest + React Testing Library tests covering key flows and behaviours.
Deploy to Vercel or Netlify with the SPA fallback configured and no client secrets, then integrate error tracking (with source maps and error boundaries reporting in) and Web Vitals performance monitoring. Verify the deployed app: deep links work, errors are captured, metrics report, and the test suite passes. This phase takes the app from feature-complete to genuinely production-grade and observable.
// Tests (Vitest + RTL) — behaviour, not implementation
it("adds a task and shows it", async () => {
render(<App />);
await userEvent.type(screen.getByLabelText(/title/i), "Ship capstone");
await userEvent.click(screen.getByRole("button", { name: /add/i }));
expect(await screen.findByText("Ship capstone")).toBeInTheDocument();
});
// Deploy + observe + secure:
// - Netlify _redirects: /* /index.html 200 (SPA deep links)
// - vite.config: build.sourcemap = true (upload maps to error service)
// - Web Vitals: onLCP/onCLS/onINP -> monitoring
// - Security: DOMPurify any injected HTML; no VITE_ secrets; server validates allEvaluation Rubric
- Integration & architecture (20%): the full toolkit integrated coherently in a clean, typed, well-structured app.
- Data & state (15%): server state via TanStack Query (cache, mutations, invalidation) and appropriate client state; React 19 features used where they fit.
- Forms, routing & UI (15%): validated forms, multi-page routing with layouts and 404, consistent styling, and purposeful accessible animation.
- Performance & resilience (15%): route-based code splitting, profiled memoisation, and error boundaries with recoverable fallbacks.
- Accessibility (15%): semantic markup, labelled/error-associated forms, focus management, and full keyboard operability throughout.
- Production readiness (20%): TypeScript safety, Vitest/RTL tests, deployment with SPA fallback and no client secrets, error + Web Vitals monitoring, and secure data handling.
Extension Challenges: Add real-time updates via WebSocket (custom hook with cleanup) reflected in the query cache; implement optimistic updates across the app with rollback on failure; add end-to-end tests (Playwright/Cypress) for critical flows alongside the unit tests; set up CI that runs tests and a production build on every pull request with preview deployments; add a Content Security Policy and audit the app with Lighthouse and an accessibility checker (axe); and share the data-layer logic with a React Native build (Module 34) to prove how portable the core is across web and mobile.