This guide is for the person running the day. It covers setup, pacing, common problems, and how to keep a mixed-ability group moving forward together.
Work through this on a clean machine if possible — ideally the same OS as attendees.
git clone <repo-url>
cd react-tutorial
npm install
npm run dev
# Should open http://localhost:5173 showing PawFinder headercd services/animals-api
npm install
npm start
# Should show: PawFinder Animals API running at http://localhost:3001Visit http://localhost:3001/animals — you should see "total": 34 with animal data.
npm run section:1:start # Should print: Reset to section 1/start
npm run section:2:start # Should print: Reset to section 2/start
# Verify sections 3 and 4 also work
npm run section:1:start # Reset back to start before the daynpm run storybook
# Should open http://localhost:6006 — mostly empty at section-1/startnpx tsc --noEmit
# Should produce no output (clean)npx playwright install chromium
npm run test:e2e
# Should show "No tests found" — that's fine at section-1/startIf anything fails, fix it before the day. Nothing kills momentum faster than a broken setup.
| Time | Activity |
|---|---|
| 09:00 | Welcome, attendee setup |
| 09:30 | Section 1: React Fundamentals |
| 10:30 | Section 2: State & Interactivity |
| 11:30 | Section 3: Atomic Design + Storybook |
| 12:45 | Lunch |
| 13:30 | Section 4: Data Fetching |
| 14:30 | Section 5: Unit & Component Testing |
| 15:45 | Buffer / catch-up |
| 16:15 | Section 6 (stretch): E2e with Playwright |
| 17:15 | Retrospective |
| 17:30 | End |
Spend 5 minutes on context:
- "Today you're building PawFinder — a real animal re-homing app. It will fetch real data, have real routing, and have real tests by the end of the day."
- Show the finished app (
npm run section:4:complete, thennpm run dev). Let attendees see what they're building toward, then reset to section 1 start. - "We've broken the day into six sections. Each one has a worksheet with step-by-step instructions. The reference solution is always one command away — you're never stuck."
Each attendee needs:
1. VS Code open on the react-tutorial folder
2. Terminal 1: cd services/animals-api && npm install && npm start
3. Terminal 2: npm install && npm run dev
4. Browser open at http://localhost:5173 — see the PawFinder header
5. npm run section:1:start (confirm: "Reset to section 1/start")
Walk the room while attendees set up. Common issues during setup are listed below. Aim to have everyone running by 09:30.
Your introduction (10 minutes):
- Explain components with an analogy: "A component is like a LEGO brick. PetCard is a brick. PetList is made of PetCard bricks. The app is made of PetList bricks."
- Show JSX: write
<h1>Hello</h1>in App.tsx, save, see it render. Then pass anameprop. This takes 3 minutes and makes the concept concrete. - Show TypeScript auto-complete working on an Animal prop — hover over a field to see its type.
- Send them to the worksheet.
Pacing signal: At 10:15, check how the room is doing. If most people have cards rendering, let them continue. If more than half are stuck, do a group walkthrough of Steps 1–4 together before sending them back.
Checkpoint moment (10:25):
Anyone not done with Section 1 should: npm run section:1:complete — they can read the solution and move on.
Your introduction (5 minutes):
- "The cards look great. Clicking 'Dogs' does nothing. Let's fix that."
- Show
useStatein the browser console: in any component, demonstrate that mutating a variable directly doesn't trigger a re-render, but callingsetStatedoes. This is the key insight. - Show the anti-pattern: "What if I stored the filtered list in state?" — draw a diagram showing two state values that need to be kept in sync. "React idiom: derive it."
Pacing signal: Search bar is fast (step 1–4, ~15 minutes). FilterBar takes longer. If attendees are building the filter logic from scratch, the 30-minute mark is a reasonable check-in point.
Your introduction (10 minutes):
- This is the most conceptual section. Don't rush the explanation.
- Draw the atom → molecule → organism hierarchy on a whiteboard (or screen). Label the existing components: "PetCard is currently a molecule that does atomic work. We're going to separate those concerns."
- Start Storybook together:
npm run storybook. Show that it's mostly empty. "By lunch you'll have a component library." - Emphasise: Storybook is not optional here. The stories are part of the deliverable, not a nice-to-have.
The reorganisation step (Step 10) tends to confuse people because they delete files that something else imports. Tell them: "TypeScript will show you every broken import the moment you delete a file — it's a free checklist."
Pacing signal: If it's 12:30 and someone hasn't started the story files yet, help them skip the folder move (just update the imports, don't move files) and focus on getting at least the Badge story working.
Good time for:
- A 5-minute retrospective on the morning ("what clicked? what was confusing?")
- Showing the Storybook component libraries of real teams to inspire what's possible
Your introduction (10 minutes):
- "Until now, the app has been lying. We've had three fake animals. That changes now."
- Demo: stop the API, refresh the app — currently it'll show nothing or an error. "We need to handle this gracefully."
- Explain the fetch lifecycle visually:
loading → (success: data) or (failure: error). Most real-world async UI work is just managing these three states. - Show
useEffectwith an empty dependency array — "this runs once, after the first render". Add aconsole.loginside to prove it.
Watch for: The custom hook step (Step 4) is where attendees sometimes get lost. The concept of "a hook is just a function that uses hooks" is simple but the abstraction jump can confuse people. If you see someone confused, say: "For now, just put the useState and useEffect directly in App.tsx — the hook is a refactor, not a requirement."
The routing bonus (Steps 5–8) is genuinely achievable in 20 minutes if the fetching part went smoothly. Encourage people to try it.
Your introduction (10 minutes):
- "Every line of code you wrote today has no tests. That means any change could silently break something."
- Live demo: introduce a deliberate bug in Badge (change
'bg-green-100'to'bg-blue-100'). Open the app — "looks fine, right?" Then write and run a test — it fails immediately. Fix the bug, test passes. - Explain the philosophy: "Test behaviour, not implementation. If your tests break when you rename a variable, they're testing the wrong thing."
- Run
npm test— explain watch mode.
MSW is already configured — don't spend time on it unless asked. The test/setup.ts file handles everything. Attendees just write tests.
The waitFor pattern is often new. Explain: "Your fetch is async — the data doesn't appear instantly. waitFor keeps retrying the assertion until it passes or times out."
Pacing signal: If it's 15:30 and someone has only written the utility test, that's fine — the utility test + one component test shows the pattern. The integration test is the stretch goal for this section.
Use this time flexibly:
- If the group is ahead: Start Section 6 early
- If the group is behind: Pair-program with anyone stuck on Section 5; it's more valuable than moving to E2e
- Q&A: Common questions at this point are "when do I use
useEffectvsuseState?", "how do custom hooks work?", and "what's the difference between unit and integration tests?" — have answers ready
Your introduction (5 minutes):
- "You've tested components in isolation. Now we test the whole journey in a real browser."
- Run
npm run test:e2e:ui— Playwright's visual runner is impressive. Show it to the room. - "This runs Chromium, loads your actual app, and clicks things. It catches problems that no unit test ever could."
This section is deliberately optional. Not everyone needs to complete it. The main day's value is sections 1–5.
Ask three questions (round-robin, one answer per person):
- What clicked today that didn't make sense before?
- What would you use on Monday morning?
- What would you do differently in the next version of this day?
Capture the answers if you can — they're gold for the next hackday.
Error: listen EADDRINUSE: address already in use :::3001
Another process is using port 3001. Find and kill it, or start the API on a different port:
PORT=3002 npm startThen update the Vite proxy in vite.config.ts to point to localhost:3002.
Tailwind v4 requires the @tailwindcss/vite plugin. Check vite.config.ts has tailwindcss() in the plugins array. If you added a class that doesn't seem to work, check for typos — Tailwind won't warn you about unknown classes.
Usually means node_modules isn't installed, or the wrong Node version:
node -v # should be v20 or higher
npm installThe @/ path alias isn't configured correctly. Check tsconfig.app.json has:
"paths": { "@/*": ["src/*"] }And vite.config.ts has the matching alias in resolve.alias.
# Clear the cache and retry
rm -rf node_modules/.cache/storybook
npm run storybook# Reset to a clean known state
npm run section:N:start # replace N with current section numberIf you want to inspect what the reset overwrote, the original files are in sections/section-N/start/ — open them in VS Code without resetting.
TypeScript errors are usually the fastest diagnosis. Press Ctrl+Shift+P in VS Code → "TypeScript: Show All Errors" to see every error in the project at once.
Check that src/test/setup.ts is referenced in vitest.config.ts under setupFiles. The setup file must call server.listen() in beforeAll.
The playwright.config.ts is configured with a webServer block that starts npm run dev automatically. If you see connection refused errors, check the url field matches your dev server port.
If you see the whole group consistently running behind by 20+ minutes, use this protocol:
- Call a checkpoint
- Everyone runs
npm run section:N:starton the next section's start branch - Explain the key new concept in 3 minutes
- Move on
No one should spend the whole day stuck on one section. The reset script exists precisely for this.
Every section has "Going further" bonus tasks. Additionally:
- Section 3: encourage them to explore the Storybook Controls panel and write stories for SearchPanel
- Section 4: encourage the enquiry form
- Section 5: encourage them to write tests for the AnimalDetailPage or test error states via MSW overrides
- Section 6: encourage the visual regression screenshots or Playwright code generation
The best thing a fast attendee can do is help someone sitting next to them — pair programming in this context is genuinely more valuable than solo bonus tasks.