Day 5: Scaling up with TypeScript
TypeScript, context, routing, server state and error boundaries turn the Reading List into a typed, multi-page app.
Over the first four days you learned the core of React: components, props, state, effects and custom hooks. That is enough to build almost any single screen. Today is about what happens when an app grows: more files, more pages, more people touching the code, and data that lives on a server.
You will add the tools that nearly every real React codebase uses. TypeScript catches mistakes as you type. Context shares state without prop drilling. React Router gives each screen its own URL. TanStack Query handles server data and caching. Error boundaries keep one bug from blanking the whole page. None of these replace what you already know. Each one solves a problem you would hit soon if you kept building.
Time plan
| Lesson | Time | What you'll do |
|---|---|---|
| TypeScript for React | 50 min | Type props, data, state, events and the reducer Action union |
| Passing data deeply with context | 35 min | Create, provide and read a context, and type it safely |
| Scaling up with reducer and context | 35 min | Build BooksProvider with useBooks and useBooksDispatch |
| Routing with React Router | 50 min | Add pages, a shared layout, URL parameters and redirects |
| Server state with TanStack Query | 45 min | Fetch and cache with useQuery, rebuild SearchSuggestions |
| Error boundaries | 35 min | Contain crashes with react-error-boundary and recover from them |
| Checkpoint | 50 min | Put it all together in your Reading List |
About 5 hours in total. Every sandbox today is TypeScript, so the files end in .ts and .tsx.
What you'll build today
By the end of the day your Reading List is a small but real app:
- written in TypeScript, with
Status,Bookand a typedActionunion forbooksReducer; - with a
BooksProviderthat owns the reducer, so any component can read books or dispatch actions; - with three routes:
/for the list,/books/:idfor a book's detail page, and/addfor the add form; - with search suggestions on the add page powered by TanStack Query's
useQuery; - with an error boundary around the detail page, so a bad link shows a friendly message instead of a blank screen.
Lessons
TypeScript for React
Props, data shapes, state, events and a typed reducer.
Passing data deeply with context
Share values with a whole subtree, no prop drilling.
Scaling up with reducer and context
BooksProvider, useBooks and useBooksDispatch.
Routing with React Router
Routes, layouts, URL parameters and navigation.
Server state with TanStack Query
Fetching, caching and refreshing remote data.
Error boundaries
Contain crashes and help users recover.
Time to upgrade your own Reading List. Work through the steps in your local project, then compare with the full code in the sandbox at the end.
Convert the project to TypeScript
The smoothest way is to start a fresh TypeScript project with Vite and move your code into it. Vite's react-ts template comes with a working tsconfig.json, so there is nothing to configure.
npm create vite@latest reading-list-ts -- --template react-ts
cd reading-list-ts
npm installCopy your src files across and rename them: components from .jsx to .tsx, plain modules like data.js and booksReducer.js to .ts. Then create src/types.ts with Status and Book, type each component's props, and give booksReducer its Action union. VS Code will underline anything that does not fit. Running npm run build also checks every file, because the template's build script runs the TypeScript compiler first.
Move the reducer into BooksProvider
Create src/BooksContext.tsx with two contexts, a BooksProvider that calls useReducer(booksReducer, initialBooks), and the hooks useBooks and useBooksDispatch. Wrap your app in <BooksProvider> and replace every books and dispatch prop with the hooks. If you want to keep Day 4's persistence, use the initializer and effect shown in the reducer and context lesson.
Add the three routes
npm install react-router@7 @tanstack/react-query react-error-boundarySplit the screen into pages: BookListPage (the filter bar and the list), BookDetailPage, and AddBookPage (the form). Add a Layout with a header, NavLinks and an <Outlet />. In BookCard, turn the title into a <Link> to /books/ plus the book's id. After adding a book, call navigate('/').
The filter now lives inside BookListPage, so it resets when you leave the page. That is fine for today. If you prefer to keep it, store it in App.
Fetch suggestions with TanStack Query
Create one QueryClient, wrap the app in <QueryClientProvider>, and rewrite SearchSuggestions with useQuery. Use ['search', query] as the key, enabled to wait for two characters, and placeholderData: keepPreviousData. Your Day 4 effect, its loading state and its cleanup can all be deleted. Picking a suggestion fills in the add form.
Protect the detail page
Wrap the detail page in an ErrorBoundary with a FallbackComponent and resetKeys={[id]}. In the code below, the detail page throws when no book has the id from the URL, so the footer link to a missing book shows the fallback instead of a blank page. Because of resetKeys, moving to a different id clears the error automatically.
Here is the complete app for Day 5. Click a title to open its page, cycle a status to done and rate it, add a book with the help of the suggestions, and try the broken link in the footer.
To run this locally, the only change is the router. In App.tsx, import BrowserRouter instead of MemoryRouter and use it in the same place. Vite's dev server already sends every URL to your app, so refreshing on /books/2 works.
Tomorrow you will meet React 19's newest features: form Actions with useActionState and useOptimistic, reading promises with use(), transitions, the React Compiler, and your first tests.
Day 5 progress