Building Components in React
Learning Outcome Guide
Section titled “Learning Outcome Guide”At the end of this class, you should be able to…
- Build structured React components using props, children, and internal state.
- Demonstrate component composition patterns for modular UI design.
- Apply Tailwind utilities to style React components consistently and maintainably.
Additional Notes:
- Keep
useStateusage simple (booleans, strings, counters). Avoid complex state until later lessons. - Encourage composition mindset: build small, focused components, then combine them.
- In React 19, new hooks like
useActionStatesimplify async UI state updates; not required here but worth mentioning as “what’s next.” - Tailwind 4 introduces
@themefor CSS-based config; continue using Tailwind 3 utilities in this course.- See Create React App - Tailwind V3 Guide - Adaptations Required, since we’re not using Create-React-App
Lesson focus
Section titled “Lesson focus”This lesson extends our React component work by moving beyond simple static examples.
In this example, we will cover:
- Passing data into components with props
- Using children to “slot” content into a component
- Using composition with a small sub-component (
ResultsItem) to reduce repeated markup
Connecting to prior lessons
Section titled “Connecting to prior lessons”In Lesson 07 we rebuilt the NAIT Resource Directory UI using React and Tailwind.
In this lesson, we keep the same UI, but work on making it more maintainable and reusable:
- Repeated markup becomes a component
- Data flows into components via props
- Components can expose “slots” using
children
Strategy
Section titled “Strategy”We will focus on one area of the UI: the Results list.
- Introduce a hard-coded
resourcesarray - Create a
ResultsItemcomponent - Refactor
Resultsto render a list ofResultsItems - Use
childrento render an optional Open Now badge
Phase 1: Add sample resource data
Section titled “Phase 1: Add sample resource data”Create a new file:
src/data/resources.js
Add the following array:
export const resources = [ { id: 'tutoring', title: 'Peer Tutoring Centre', category: 'Academic', summary: 'Drop-in tutoring and study support.', location: 'Building W, Room W101', openNow: true, }, { id: 'counselling', title: 'Counselling Services', category: 'Wellness', summary: 'Confidential mental health supports.', location: 'Virtual and in-person', openNow: true, }, { id: 'bursaries', title: 'Student Awards and Bursaries', category: 'Financial', summary: 'Funding options and application help.', location: 'Student Services, Main Floor CAT', openNow: false, }, { id: 'it', title: 'IT Service Desk', category: 'Tech', summary: 'Account access, Wi-Fi, BYOD support.', location: 'Library', openNow: true, },];Phase 2: Create a ResultsItem component
Section titled “Phase 2: Create a ResultsItem component”Create a new component file:
src/components/ResultsItem.jsx
This component will:
- Accept props for the resource data
- Render a consistent layout
- Use
childrenfor optional content (the badge)
Start with this:
export default function ResultsItem({ title, category, summary, location, children }) { return ( <li className="px-4 py-3 text-gray-900 hover:bg-gray-50"> <div className="flex items-start justify-between gap-3"> <h2 className="text-sm font-semibold">{title}</h2> <div className="flex items-center gap-2"> {children} <small className="text-xs text-gray-500">{category}</small> </div> </div>
<p className="mt-1 text-xs text-gray-500">{summary}</p>
<small className="mt-1 block text-xs text-gray-500">{location}</small> </li> );}What to notice:
- Props flow into the component as inputs
childrenis rendered in a specific spot, making it a simple “slot”
Phase 3: Refactor Results to compose ResultsItem
Section titled “Phase 3: Refactor Results to compose ResultsItem”Open src/components/Results.jsx.
- Import the resources data.
- Import
ResultsItem. - Map the array and render one
ResultsItemper resource.
Here is a complete example:
import ResultsItem from './ResultsItem';import { resources } from '../data/resources';
export default function Results() { return ( <section className="h-full mb-4"> <div className="h-full rounded border border-gray-200 bg-white shadow-sm"> <div className="flex items-center justify-between border-b border-gray-200 px-4 py-3"> <strong className="text-sm text-gray-900">Results</strong> <span className="rounded-full bg-gray-200 px-2.5 py-0.5 text-xs font-semibold text-gray-700"> {resources.length} </span> </div>
<ul className="divide-y divide-gray-200"> {resources.map((r) => ( <ResultsItem key={r.id} title={r.title} category={r.category} summary={r.summary} location={r.location} > {/* children: optional badge content */} {r.openNow && ( <span className="rounded-full bg-emerald-100 px-2 py-0.5 text-[10px] font-semibold text-emerald-800"> Open now </span> )} </ResultsItem> ))} </ul> </div> </section> );}This is the key Lesson 08 idea:
Resultscomposes the list from smaller components- Data flows into
ResultsItemusing props - Optional UI is inserted into
ResultsItemusingchildren
Assessment
Section titled “Assessment”ResultsItemexists and is used byResults- Props are used to pass resource data into
ResultsItem childrenis used to render an optional “Open now” badge
Student exercise
Section titled “Student exercise”-
Add one more optional badge using children, for example:
- A category-specific badge for
Tech - A “Popular” badge for one item
- A category-specific badge for
-
Add a prop to
ResultsItemfor an optional value, for example:isVirtual(boolean)hours(string) Render it only when provided (conditional rendering)
Stretch goal:
- Add an
isSelectedprop and conditionally apply a highlight class when it is true
Push to your GitHub workbook repo
Section titled “Push to your GitHub workbook repo”- Stage all changes:
git add -A- Commit:
git commit -m 'Lesson 08 - ResultsItem, props, children'- Push:
git push origin main