Purpose of this document: This is a comprehensive spec for an AI agent (or human author) to generate all content, code, and instructions for a one-day React hackday event. The agent should produce: a facilitator guide, per-section attendee worksheets, starter code, completed reference code, Storybook stories, test files, and a mock API. Instructions for exactly what to produce are at the bottom of each section.
Name: PawFinder Hackday — Learning React by Building an Animal Re-Homing App
Duration: One day (~6 hours of active coding, ~7 hours with breaks)
Format: Guided workshop with self-paced sections. Each section has a defined start point and a complete reference so attendees can catch up at any point.
Goal: Attendees leave with practical React skills, an understanding of component-driven design, confidence writing tests, and a working mental model of how React apps are structured in professional teams.
- Audience: Mixed — ranges from developers new to web/JS through to JS developers who haven't used React before
- All attendees have: VS Code installed, Node.js (v20+) installed and on PATH, Git installed
- No attendee needs to: Configure build tools, install Storybook, or set up testing infrastructure — this is all pre-done in the starter repo
- Tone for instructions: Friendly and explanatory. Assume nothing about React knowledge. Do assume familiarity with basic programming concepts (variables, functions, loops). Where a concept might be new, explain the "why" before the "how."
An animal re-homing platform. Users can browse animals available for adoption, filter by species/age/size, view individual animal profiles, and submit an adoption enquiry.
- Lists & filtering → state management, array methods
- Pet cards → reusable component composition, props
- Pet detail page → routing, data fetching
- Enquiry form → controlled inputs, form state, validation
- Species/status badges → atoms in Atomic Design
- Loading/error states → real-world async patterns
Animals have a rich data shape designed to support both the hackday UI and a future LLM Q&A feature:
| Field | Type | Purpose |
|---|---|---|
id, name, species, breed |
string | Core identity |
age, ageGroup, size, gender |
string/number | Filterable attributes |
neutered, vaccinated, microchipped |
boolean | Health admin |
status |
available / reserved / adopted |
Availability |
description |
string | 2–3 sentence UI headline |
story |
string | Rich background/history — primary LLM context |
personality |
string[] | Tags (e.g. ['gentle', 'playful']) |
goodWith, notGoodWith |
string[] | kids, dogs, cats, small-animals |
careNeeds |
{ exercise, grooming, experience } |
low/medium/high, first-time/experienced/expert |
healthNotes |
string | Conditions, diet, management — LLM context |
rehomingAdvice |
string | Ideal home description — primary LLM context |
location |
string | Manchester area location |
imageUrl |
string | picsum.photos/seed/{id}/400/300 |
dateArrived, adoptionFee |
string / number | Admin |
The story, healthNotes, and rehomingAdvice fields are intentionally verbose to give a future LLM enough context to answer questions like "which dog suits a flat?" or "tell me about Max's history?"
| Tool | Purpose | Version target |
|---|---|---|
| Vite | Build tooling & dev server | Latest |
| React | UI framework | 19.x |
| TypeScript | Type safety | 5.x |
| React Router | Client-side routing | v7 |
| Storybook | Component development & documentation | 8.x |
| Vitest | Unit & component test runner | Latest |
| React Testing Library | Component testing utilities | Latest |
| Playwright | End-to-end browser testing | Latest |
| MSW (Mock Service Worker) | API mocking in tests | v2 |
| Express | Mock microservice API | 4.x |
| Tailwind CSS | Styling | v4 |
Why Tailwind: Removes CSS decision fatigue so attendees focus on React concepts. Utility classes are readable enough to follow without prior Tailwind experience.
The starter repo should be a monorepo with the following layout:
pawfinder/
├── apps/
│ └── web/ # The React app (Vite + React + TS)
│ ├── src/ # Active working directory — attendees edit files here
│ │ ├── components/ # Atomic Design folders (atoms/ molecules/ organisms/ templates/)
│ │ ├── pages/ # Route-level components
│ │ ├── hooks/ # Custom React hooks
│ │ ├── services/ # API client functions
│ │ ├── types/ # TypeScript interfaces
│ │ └── test/ # Test utilities and MSW handlers
│ ├── .storybook/ # Storybook config
│ ├── e2e/ # Playwright tests
│ └── sections/ # Read-only reference snapshots (never edit these)
│ ├── section-1/
│ │ ├── start/ # src/ state at the beginning of section 1
│ │ └── complete/ # Reference solution for section 1
│ ├── section-2/
│ │ ├── start/
│ │ └── complete/
│ ... (sections 1–6)
│
└── services/
└── animals-api/ # Express mock microservice
├── src/
│ ├── data/ # Seed data (30-40 animals)
│ └── routes/ # Express route handlers
└── package.json
Attendees work entirely in apps/web/src/. They never need to use Git commands during the day.
The repo provides npm scripts that copy a section snapshot over the working src/ directory:
npm run section:1:start # resets src/ to the beginning of section 1
npm run section:1:complete # resets src/ to the finished state of section 1
npm run section:2:start # resets src/ to the beginning of section 2
# ... and so onThese scripts simply overwrite src/ with the contents of the matching sections/section-N/start|complete/ folder. No Git knowledge required, no conflict risk, works identically every time.
Catching up: If an attendee falls behind, they run npm run section:N:start for the current section and are immediately back on track — no facilitator intervention needed.
Reading the reference: Attendees can open sections/section-2/complete/ directly in VS Code to read the reference code without affecting their working files.
Script implementation: Each script should be a small Node.js script (not a shell script, for cross-platform compatibility on Windows) that uses fs to copy the snapshot directory over src/. A single scripts/reset-section.mjs can handle all variants, called with args from package.json.
A simple Express server. This is pre-built and provided — attendees do not write or modify it. It exists so that fetch() calls feel real and connect to something running locally.
| Method | Path | Description |
|---|---|---|
| GET | /animals |
List all animals. Supports query params: species, size, status, search |
| GET | /animals/:id |
Single animal by ID |
| POST | /enquiries |
Submit an adoption enquiry. Body: { animalId, name, email, message } |
cd services/animals-api
npm install
npm start
# Runs on http://localhost:3001The web app's Vite config should proxy /api to localhost:3001 so fetch calls are just /api/animals.
Generate 30–40 animals across: dogs (12), cats (10), rabbits (6), guinea pigs (4), other (2–4). Mix of statuses: ~70% available, ~20% reserved, ~10% adopted. Include variety in age, size, and "good with" flags. Use placeholder image URLs from a service like picsum.photos with consistent per-animal IDs so images are stable.
| Time | Block | Duration |
|---|---|---|
| 09:00 | Welcome, setup check, repo clone | 30 min |
| 09:30 | Section 1: React Fundamentals | 60 min |
| 10:30 | Section 2: State & Interactivity | 60 min |
| 11:30 | Section 3: Atomic Design + Storybook | 75 min |
| 12:45 | Lunch | 45 min |
| 13:30 | Section 4: Data Fetching & the API | 60 min |
| 14:30 | Section 5: Unit & Component Testing | 75 min |
| 15:45 | Buffer / catch-up / Q&A | 30 min |
| 16:15 | Section 6 (stretch): E2e with Playwright | 60 min |
| 17:15 | Wrap-up, retro, next steps | 15 min |
Each section below defines: learning goals, concepts covered, exactly what code attendees write, what the start state contains, and what the complete state looks like. The agent should produce attendee instructions and all code for each section.
Branch: section-1/start → section-1/complete
Duration: 60 minutes
- Understand what React is and why it exists (the "component" mental model)
- Write JSX and understand how it relates to HTML
- Create functional components and pass data via props
- Compose components together
- JSX is not HTML — it compiles to JavaScript function calls
- Components are just functions that return JSX
- Props are how you pass data into a component (like function arguments)
- Components should do one thing well (single responsibility, foreshadowing Atomic Design)
- A working Vite + React + TypeScript + Tailwind setup
App.tsxrenders a single<h1>PawFinder</h1>with a basic nav shelltypes/animal.tsalready contains theAnimalTypeScript interface (so attendees don't need to design the data shape)- The animals API is running and can be hit manually but is not yet wired to the UI
- A static array of 3 hardcoded animal objects in
App.tsxfor the section to work with
- A
PetCardcomponent insrc/components/PetCard.tsxthat accepts anAnimalprop and renders: name, species, breed, age, size, location, status badge, and a placeholder image - A
PetListcomponent insrc/components/PetList.tsxthat acceptsanimals: Animal[]and renders a grid ofPetCardcomponents - Wire
PetListintoApp.tsxpassing the hardcoded array
PetCard.tsx— fully implemented, styled with TailwindPetList.tsx— grid layout rendering multiple cardsApp.tsx— renders<PetList animals={hardcodedAnimals} />- No interactivity yet; data is still hardcoded
- Show that moving JSX into a component doesn't change what renders — it just organises the code
- Show TypeScript catching a missing prop as a teachable error
- Foreshadow: "later we'll replace the hardcoded array with real API data"
Branch: section-2/start → section-2/complete
Duration: 60 minutes
- Understand the concept of state and why React re-renders on state change
- Use
useStateto make components interactive - Handle user events (clicks, text input)
- Filter a list based on state
- State vs props: props come from outside, state lives inside the component
- Why you can't just mutate a variable — React doesn't know about it
useStatereturns a value and a setter; calling the setter triggers a re-render- Derived values: filter the list in render rather than storing filtered data in separate state
- Identical to section 1 complete:
PetCard,PetList, hardcoded animals inApp.tsx
- A
SearchBarcomponent with a text input, wired viaonChangeto a handler - A
FilterBarcomponent with species filter buttons (All / Dogs / Cats / Rabbits / Other) - State in
App.tsx:searchQuery: stringandselectedSpecies: string | null - Filter logic: derive the displayed list by filtering
animalsbased on both state values before passing toPetList - Clear/reset button that resets both state values
SearchBar.tsx— controlled input componentFilterBar.tsx— species filter with active state stylingApp.tsx— manages filter state, derives filtered list, passes to PetList- Searching "buddy" shows only matching animals; selecting "Cats" shows only cats; combining both works
- Live demo: set state to a value, show it re-renders
- Show the anti-pattern of storing derived data in state and why it creates sync bugs
- Explain controlled vs uncontrolled inputs briefly
Branch: section-3/start → section-3/complete
Duration: 75 minutes
- Understand the Atomic Design mental model (atoms → molecules → organisms)
- Refactor existing components into the atomic hierarchy
- Write Storybook stories to document and test components in isolation
- Appreciate how a component library grows from primitives
Atomic Design (keep it practical, not dogmatic):
- Atoms: Smallest UI units that can't be broken down further — Button, Badge, Input, Avatar
- Molecules: Combinations of atoms that form a recognisable UI pattern — PetCard (Badge + Image + text)
- Organisms: Complex UI sections composed of molecules — PetList (many PetCards), FilterBar + SearchBar together as a SearchPanel
- Templates/Pages: Layout and composition — not covered in depth today
- Emphasise: this is a mental model for organising components, not a strict rule system
Storybook:
- Storybook lets you develop and document components in isolation, without running the full app
- A "story" is just a snapshot of a component in a particular state
- Great for: design review, catching regressions, onboarding new devs, QA
- Section 2 complete, plus: Storybook already configured and running (
npm run storybook) - A single example story file already exists as a reference (
Button.stories.tsxwith a placeholder Button atom already implemented) - Component files are still flat in
src/components/
- Reorganise
src/components/intoatoms/,molecules/,organisms/subdirectories - Extract a
Badgeatom from the status badge insidePetCard— it acceptslabelandvariant(available/reserved/adopted) props - Extract an
Avataratom for the pet image with fallback handling - Write Storybook stories for
Badge: one story per variant (Available, Reserved, Adopted) - Write a Storybook story for
PetCardin its default state and one with a "Reserved" status - Optionally: write stories showing PetCard in a loading skeleton state
atoms/Badge.tsx+atoms/Badge.stories.tsxatoms/Avatar.tsx+atoms/Avatar.stories.tsxmolecules/PetCard.tsx+molecules/PetCard.stories.tsxorganisms/PetList.tsx(updated imports)- Storybook shows all stories; clicking through them works without the API running
- Run Storybook and show how you can change Badge variant and see it live without the app
- Show how Storybook makes it easy to test edge cases (very long names, missing images) that are hard to reproduce in the full app
- Show the Controls panel in Storybook for interactive prop editing
Branch: section-4/start → section-4/complete
Duration: 60 minutes
- Use
useEffectto trigger side effects after render - Fetch data from the mock API and render it
- Handle loading and error states gracefully
- Understand why custom hooks are useful for encapsulating fetch logic
- Side effects in React: things that reach outside the component (network, timers, subscriptions)
useEffect(fn, deps)— runs after render; the dependency array controls when it re-runs- Why you can't make the component function itself async
- The fetch lifecycle: loading → success or error
- Custom hooks (
useAnimals) as a way to reuse and test data-fetching logic separately from UI
- Section 3 complete
src/services/animalsApi.tsalready exists with typed wrapper functions (getAnimals,getAnimalById,submitEnquiry) — attendees use these, not raw fetch- The animals API is running on
localhost:3001, Vite proxy is configured
- A custom hook
useAnimals(filters)insrc/hooks/useAnimals.tsthat:- Accepts filter params (search, species)
- Returns
{ animals, isLoading, error } - Fetches from the API on mount and when filters change
- Update
App.tsxto useuseAnimalsinstead of the hardcoded array - Add a loading state to
PetList— render skeleton cards while loading - Add an error state to
App.tsx— friendly error message if the API is unreachable - Bonus: add routing so clicking a PetCard navigates to
/animals/:id, with a basicAnimalDetailPagethat fetches a single animal
hooks/useAnimals.ts— encapsulates fetch logicApp.tsx— uses the hook, handles loading/errorPetList.tsx— renders skeletons when loading- (Bonus)
pages/AnimalDetailPage.tsxand React Router configured inApp.tsx
- Break the API on purpose (stop the server) and show the error state catching it
- Show the network tab in DevTools during a fetch
- Explain why the dependency array matters: add
filtersand watch it re-fetch on filter change
Branch: section-5/start → section-5/complete
Duration: 75 minutes
- Understand the difference between unit tests and component tests
- Write unit tests for pure logic with Vitest
- Write component tests with React Testing Library
- Use MSW to mock API calls in tests without coupling to the real server
- Know what to test and what not to test
Testing philosophy first:
- Unit tests: test a function in isolation (filter logic, data transformations)
- Component tests: render a component, interact with it, assert on output — tests from the user's perspective
- Don't test implementation details (internal state, private functions)
- Test what the user sees and does, not how the component works internally
Tool roles:
- Vitest: the test runner (replaces Jest, same API)
- React Testing Library: renders components and provides queries like
getByRole,getByText - MSW: intercepts
fetchcalls at the network level and returns mock responses — tests are isolated from the real API
- Section 4 complete
- Vitest, React Testing Library, and MSW already configured
src/test/msw-handlers.tsalready has MSW handlers returning mock animal data — attendees use these but can add to them- One example passing test already exists to show the pattern
Unit tests (src/services/animalsApi.test.ts or a utils file):
- Test the filter/search logic — given an array of animals and a query, does filtering return the right results?
- Test the
formatAgehelper (e.g.1 → "1 year",3 → "3 years")
Component tests:
3. Badge.test.tsx — renders with correct label; applies correct colour class per variant
4. PetCard.test.tsx — renders animal name, species, breed; shows "Reserved" badge for a reserved animal; renders a link to the animal's detail page
5. SearchBar.test.tsx — typing in the input calls onSearch with the typed value
6. PetList.test.tsx — renders the correct number of cards; shows a loading skeleton when isLoading is true; renders an empty state message when no animals match
Integration-level component test:
7. App.test.tsx (or a PetListPage.test.tsx) — MSW intercepts the API call, the list renders with mocked data, filtering by species updates the displayed list
- All test files above, all passing
npm testruns green
- Run a test, make it fail by breaking the component, fix it — show the loop
- Show MSW intercepting a network request (check the console logs)
- Discuss: "should I test this?" — the answer is usually "test the behaviour, not the implementation"
Branch: section-6-stretch/start → section-6-stretch/complete
Duration: 60 minutes (optional — for attendees who finish early or teams that want to continue)
- Understand the role of E2e tests in a testing strategy
- Write a Playwright test that drives a real browser
- Navigate the UI, interact with elements, and assert on page state
- Know the trade-offs: E2e tests are slower and more brittle than component tests
- The testing pyramid: many unit tests → fewer component tests → fewer still E2e tests
- E2e tests verify user journeys, not individual components
- Playwright launches a real browser; tests interact the same way a user would
- These tests run against the real app + real API (or a test instance)
- Section 5 complete (full app working)
- Playwright installed and configured, one example test exists
- A test that loads the home page and asserts the heading "Find Your Perfect Pet" is visible
- A test that types "golden" in the search bar and asserts only matching animals are shown
- A test that clicks "Cats" filter and asserts all visible pet cards show species "Cat"
- A test that clicks on a pet and asserts the detail page loads with the correct animal name
- Bonus: a test for the adoption enquiry form — fill it in, submit, assert success message
- All 4+ E2e tests passing via
npx playwright test - A Playwright HTML report generated
The agent generating content should also specify or generate the following infrastructure, which must be in place before the day:
- Vite config with
/apiproxy tolocalhost:3001 - Tailwind v4 configured with a basic design token set (brand green for PawFinder, neutral grays)
- TypeScript strict mode on
- Path aliases configured:
@/maps tosrc/ - Storybook 8 configured with Vite builder, Tailwind addon, and accessibility addon
- Vitest configured with jsdom, React Testing Library setup file, and
@testing-library/jest-dommatchers - MSW v2 configured with a service worker for browser use and Node integration for tests
- Playwright configured targeting Chrome, with base URL
http://localhost:5173 -
npm run devstarts the app;npm run storybookstarts Storybook;npm testruns Vitest;npm run test:e2eruns Playwright
- Express server with CORS enabled for
localhost:5173 - 30–40 seed animals matching the data spec above
- All endpoints working and returning correct shapes
-
npm startruns on port 3001 - A
README.mdexplaining what it does and how to start it
The agent should generate a separate facilitator guide containing:
-
Setup pre-flight (night before): Confirm the repo is cloned and working on a test machine. Confirm the API starts. Confirm Storybook builds. Ensure the
sections/branches are all present and checked out cleanly. -
Common attendee problems and fixes:
- Port 3001 already in use → how to change the API port and update the proxy
- Storybook won't start → clear
.storybook/cache - TypeScript errors on first checkout → run
npm installagain; check Node version - "My code is broken and I don't know why" → run
npm run section:N:startto reset to a clean state
-
Pacing guidance: If the group is running behind by more than 20 minutes, direct them to
git checkout section-N/starton the current section's complete branch and move on. No one should be blocked on one section. -
For faster attendees: Every section has at least one bonus task. Faster attendees should also be encouraged to explore Storybook Controls, add more stories, or write additional tests.
-
End of day: Leave 15 minutes for a retro. Ask: what clicked? what was confusing? what would you use on Monday?
You are an AI agent tasked with generating the full content for this hackday. Use this spec to produce the following deliverables:
Generate all code for apps/web/ and services/animals-api/ as described. This includes:
- All component files (start and complete versions for each section)
- All test files
- All Storybook story files
- All configuration files (vite.config.ts, .storybook/, vitest.config.ts, playwright.config.ts, tailwind config)
- The full Express API with seed data
Code requirements:
- TypeScript throughout, no
anytypes - Tailwind for all styling
- Named exports for components
- Components accept typed props interfaces
- Tests use React Testing Library best practices (query by role/label/text, not by test-id where avoidable)
- Stories use the Component Story Format 3 (CSF3) with
satisfies Meta<typeof Component>
For each section, produce a markdown worksheet that:
- Opens with a 2-sentence "what you'll build" summary
- Explains key concepts briefly with a diagram or ASCII art where it helps
- Gives step-by-step numbered instructions
- Uses code snippets to show what to write (but doesn't just dump the whole solution)
- Has a "check your work" section with what the UI should look like/do
- Has a "going further" bonus tasks section
- Ends with a "what's next" one-liner bridging to the next section
Tone: friendly, explanatory, encouraging. Assume the reader is intelligent but brand new to React.
A single markdown document covering the items listed in the Facilitator Guide Notes section above. Include a minute-by-minute suggested schedule for how to introduce each section.
A top-level README.md that:
- Explains what PawFinder is
- Lists prerequisites
- Has "Getting started" steps (clone, install, start API, start app, checkout first section)
- Has a table of contents linking to each section worksheet
- Is friendly enough that a nervous attendee can follow it at 9am without coffee
These are recorded here so the content agent understands the reasoning behind choices and can maintain consistency:
| Decision | Rationale |
|---|---|
| Pre-scaffold over scaffold-on-day | Saves 45–60 min of setup friction; configuration errors derail beginners |
| Tailwind over CSS Modules | Removes the "how do I style this" question; classes are visible in JSX |
| Vitest over Jest | Same API, faster, native Vite integration — no config overhead |
| MSW for test mocking | Tests the real fetch path; avoids mocking fetch/module internals which is fragile |
| Single Express API not microservices | Attendees focus on React; one API to start is enough |
| npm reset scripts for sections | No Git knowledge needed on the day; no conflict risk; npm run section:N:start resets instantly |
| Animal re-homing theme | Relatable domain; obvious CRUD; variety of UI patterns; emotionally engaging |
| Atomic Design as mental model | Gives language for component thinking without being prescriptive |
| E2e as stretch | Ensures most attendees finish through testing; E2e is slower to write and run |
Spec version: 1.0 | Event date: TBD | Prepared: 2026-06-25