Day 6: Modern React 19
Suspense and use(), form Actions, optimistic UI, transitions, animated view transitions, the React Compiler and testing.
By the end of Day 5 your Reading List works: it has types, shared state, routes and server data. Today you make it feel good. You'll stop writing loading flags by hand, make the add-book form update instantly, keep the screen responsive while React does slow work, and animate the changes between screens.
These features come now, rather than on Day 2, because each one builds on things you already know: state, effects, context and async functions. They're also the newest parts of React. Most of them arrived in React 19 (December 2024), and <ViewTransition> became stable in React 19.3 (September 2026). You finish the day by switching on the React Compiler and writing your first automated tests, so you can change the app on Day 7 without being afraid of breaking it.
Time plan
| Lesson | Time | What you'll do |
|---|---|---|
| Suspense and use() | 50 min | Read a Promise with use() and show a fallback while it loads |
| Actions and forms | 60 min | Submit forms with Actions, show validation errors, and add items optimistically |
| Transitions | 45 min | Keep typing and clicking smooth with useTransition, useDeferredValue and <Activity> |
| View transitions | 45 min | Animate items entering, leaving and moving with <ViewTransition> |
| Performance and the Compiler | 40 min | Measure first, then let the React Compiler memoize for you, and meet useEffectEvent |
| Testing | 50 min | Set up Vitest and Testing Library and test a reducer and a component |
| Checkpoint | 30 min | Bring it all into your Reading List |
That adds up to about 5 hours. If the day runs long, finish the testing lesson tomorrow morning before you start Day 7.
What you'll build today
Your Reading List gets five upgrades:
- The add-book form uses an Action.
useActionStateshows validation errors, anduseOptimisticputs the new book on screen immediately, before the fake server answers. - The detail page reads its data with
use(), and a<Suspense>boundary shows "Loading details..." while it waits. - Switching filters is a Transition, and each card is wrapped in
<ViewTransition>, so cards fade in and out instead of jumping. - The React Compiler is on, so you never have to write
useMemooruseCallbackby hand. - Vitest and Testing Library check
BookCardandbooksReducerevery time you runnpm test.
Lessons
Suspense and use()
Read async data during render and declare loading states with Suspense.
Actions and forms
Form actions, useFormStatus, useActionState and useOptimistic.
Transitions
Urgent vs. non-urgent updates, useDeferredValue and Activity.
View transitions
Animate enter, exit and update with ViewTransition and transition types.
Performance and the Compiler
Profiling, memoization, the React Compiler and useEffectEvent.
Testing
Vitest, jsdom and Testing Library for components and reducers.
Today's checkpoint has two parts. The first part runs in the browser: the sandbox below holds the Day 6 version of the app. The second part (the Compiler and the tests) needs real tooling, so you do it in your own project in your editor.
What changed since Day 5
To fit in one sandbox, this version makes three simplifications. In your own project, keep what you built on Day 5.
- No router. A
selectedIdstate stands in for the/books/:idroute. In your project,BookDetailgoes inside your detail route and readsidfromuseParams(). - No TanStack Query or
localStorage. They don't change today, so leave them as they are in your project. react-error-boundaryfor the error boundary. This is the same package you used on Day 5, so nothing new to learn here.
Step 1: The add-book form becomes an Action
Open AddBookForm.tsx. The controlled inputs from Day 3 are gone. The form passes formAction from useActionState to its action prop. The action validates the fields, shows the book straight away with the onOptimisticAdd function it receives as a prop, waits for saveBook, and then dispatches added inside startTransition. The submit button is its own small component, so it can call useFormStatus.
Step 2: The list shows optimistic books
ListPage in App.tsx calls useOptimistic(books, ...). While a save is pending, the new book is in the list with pending: true, so it shows faded. When the save finishes, the real book from the reducer replaces it.
Step 3: Filters switch inside a Transition, and cards animate
handleFilterChange wraps setFilter in startTransition. Each card sits inside <ViewTransition key={book.id}>, so React cross-fades cards in and out when the filter changes. Deleting a book is also a Transition, so its card fades away.
Step 4: The detail page suspends
Click Details on a book. BookExtras calls use(fetchBookDetails(book.id)). fetchBookDetails caches one Promise per id, which is important, as the first lesson explains. Click Back: because the list sits in <Activity>, your filter is still selected.
Try these things in the sandbox:
- Submit the form with an empty author. The error appears and the title you typed stays in its input.
- Add a real book. It appears faded at once and becomes solid about a second later.
- Switch between filters and watch the cards fade. If nothing animates, your browser may not support the View Transitions API yet. Everything still works; the cards change without animation.
- Open a book, wait for the details, go back, then open the same book again. The second time there's no loading message, because the Promise is cached.
Step 5: Turn on the React Compiler (in your project)
In your Vite project, install the compiler and add it to vite.config.ts. The performance lesson explains both setups. This one is for @vitejs/plugin-react version 6 or newer:
npm install -D --save-exact babel-plugin-react-compiler@latest
npm install -D @rolldown/plugin-babelimport { defineConfig } from 'vite';
import react, { reactCompilerPreset } from '@vitejs/plugin-react';
import babel from '@rolldown/plugin-babel';
export default defineConfig({
plugins: [
react(),
babel({
presets: [reactCompilerPreset()],
}),
],
});Restart npm run dev, open React DevTools, and check that your components show the Memo badge.
Step 6: Add tests (in your project)
Follow the testing lesson to install Vitest and Testing Library, then add src/booksReducer.test.ts and src/BookCard.test.tsx. When you're done, this command should pass:
npm testDone?
Your Reading List now has optimistic adds, validation errors, a suspending detail page, animated filters, compiler-optimized components and a test suite. Tomorrow you move it to Next.js, where the Actions you wrote today can run on the server.
Day 6 progress