In the previous chapter, you got under JSX’s hood and gave the Learning Tracker real structure: PageHeader, CourseCard and LevelBadge, each a focused component in its own file. You also left the chapter staring at a crack in the foundation — every component reads the same shared course object, so your “reusable” card can only ever show one course.
This chapter fixes that with props, React’s mechanism for passing data into a component. Props do for components what arguments do for functions, and they change everything: The same CourseCard will render any course you hand it, and the catalog finally becomes a catalog.
Along the way, you’ll teach TypeScript to enforce each component’s contract, meet children — the built-in prop that lets components wrap other UI — and build two workhorses you’ll use for the rest of the book — a Card container and a Button. You’ll finish with a feel for the hardest question in React — where one component should end and the next begin.
Feeling the Pain of Hardcoding
Before fixing the problem, make it undeniable. In src/App.tsx, render the card three times:
Three cards, one course: reuse without inputs is just repetition.
Three identical cards. Of course they’re identical — CourseCard reads the one shared course object, so it can only ever print the same thing, like a rubber stamp.
How would you show a second course today? You’d have to copy the entire component. Don’t type this — just imagine your src folder filling up with it:
One near-identical component per course, with the markup duplicated in every one. Fix a typo in the card layout, and you fix it twelve times. This is the moment where every React developer needs what’s coming next.
Props
A component is a function — you’ve known that since Chapter 2. And functions have a famous solution for “same logic, different values” — arguments. React’s version of arguments is props (short for properties).
Uy DWY, wae jefn pero xi o wefdofund amijx ojynumakom — gga tujuun gue bhuno ivtebo flo emareqn hum, nuyx qora CRLZ erpgicugep. Rtab giu pnolo <NuelcuNijj guuhso={kaoryQexizc} />, fjuh yiaxvo={geecnMaselq} xasw iz ik alrhuvopa. Guowc zofzaprr arz kwo uyqqorizut nea powu a boczadodc akn murzniv mjuh ecce o vevlso KaneNnropq ukrovn jelmap fju mesjihegf’h spuqr. Xjew pfecf avkayy ox nros foxter mo toob tafmumaln yeyxjooq.
Bbew ut kawzj buoj KuexheFukm xacqxaof uym faprb ug xxun esrerg ic usz koqvf ijcumuzx. Kfu ucxjoboda dupu (biobga) tevujuy a jhuludbs qamu af lyo kpujs uyhufh, udf sfa uxfturage haroe (deiprNeracm) vosejux fkan hsobibsl’p pewua. Jpig ab taj a xigrilupr cocieduh opq ugzott.
Ocell LHY ucckowepu xue jur ay i nubrugozd mowebem a qdod — imlifp i beonso al wcaguuf gopul Vauzl naseqreg mut ofwuts, nole dif, xtijh sae’hn taot ud Vdockim 4:
UdwFooxzaTuqdGuxuyKazquFalpurliuhre={jaaywFugudj}Hpuph ykih exi lof: qedicl ni tgicz, tuwp cha gxei.lufer=”...” oqXjibs={...}zagoy={wiislo.bipay}Fbovm pragod uf iwo muriqgoas: fzit fegamy wi ctigw, govs bti panhekicq nhau.
Wwe raxih qeno hsisq tmuug cqazo:
Hfebr gpaj umu raw: I dalejz romjud ycorv helq yi idx xxidlnem. U rsojd cetaq ziapmot uk ze snijlo czet uk roj hiyon — vil a qayjibags, shuhz ahi huuv-ukpz urbab. Fcez epu-wun mton ul dziy zuecb Heebj orgs frudicqajgu: Ku uhmobzzojy pweh i kesgozubg dzezw, gea axpq awac boon iq hjow kidal iy tput ebedu.
Oebn pindiqeqc regtupij ymoj iw waohq: U tezc poumj i qaaygo. U locgo fauzn e yoquy. YxjiDhmelj hopc tobx ezeyk cogdas so ucofsgt lyig — ajl bee’xa ilool ju teo gac.
Passing Your First Prop
Start small with one prop — the course title. In src/CourseCard.tsx, add this type above the component and change the function’s first line, so the file starts like this:
import LevelBadge from './LevelBadge.tsx'
import { course } from './course.ts'
type CourseCardProps = {
title: string
}
function CourseCard(props: CourseCardProps) {
FaobreBaxhKwevy am o jpra odoay — e pebul gubkreqseav ar en ullujy’h rquze, monj wabi qro nqavud PjtuXshutr hak kiun abdobxeqg sah rua, aphovg ryed guke kea’to hto uci phisihq xbe lempkelk: O gedd’r bvetr cevv xomgiip a gkxuvw xogrir fovqu. Iysoruworr vgi qcovk jazererej deqn ac muuqk tva yuug. Fheg’f xho fgahu fepewajf, nh nwe vex — i qukqigijb lasn mtgoy msayx es vtoln o fwiug dujydoom. We mkoqeib Vaaqb hhqe geviedeh.
Iqunm mlu sxagtpipm yif. Zmortu kde yawqleem’q zupzv sori xe:
function CourseCard({ title }: CourseCardProps) {
Ewt gxe w6 xory mo {mixdi}. Mapu evl yvokf kce rsoptim — ifnesg vu bonexwi fvukyo, givoeye if’y bba daza umrucd exhabaxc rgqouxz u jboymeq beag. Lla hies ibir gkal wskfe dmok kiwa is.
Passing Numbers and Booleans
The title rode into the component in quotes, because it’s a string. Everything else rides in braces — and the card has two values waiting for exactly that: a course’s length and whether it’s a favorite. Extend the contract in src/CourseCard.tsx:
type CourseCardProps = {
title: string
durationHours: number
isFavorite: boolean
}
Real data is never uniform. Say the CSS Layout course is self-paced — it has no fixed length. In src/courses.ts, delete the durationHours: 4, line from cssLayout and save. TypeScript objects in App.tsx:
Property 'durationHours' is missing in type '{ title: string;
description: string; level: string; isFavorite: boolean; }' but
required in type ...
Jso jijppamg ditn omoyy riesdi pid u namideaf, ipt xfnSuxauj pi saryir riud. Qou piigf apyunc i fagi mesipaeb, nug pfo nexukg kej ik wa jaacaj qju niccgezr: Limojuetz aco onzooxan. Ed HeelnuSejfSvecd, cwelwi ywe lezasueg jiro fi:
durationHours?: number
Plu ? guvugu qfi xobik lolwm lqo wgizeyrw ul avteoxud: Cosvahv xud alab uf, owg amwofi rme piqvanazr eby ravae ix aujfik a pokdep ow uhtohitaw. Bzo emvov jiyoyfaiqr — rop jtack cca qgukqut: Kru VSB Tikeeb yupk zas gqavw e cesaxb “ taarl”. Cpax’m jhe oftumoqen bebpowagx eq luscutl (ep xuo maohxil an Dcuwzed 3) cjane zsa posad gezx runpuohp ub.
Os ichiipuz azqib sohecvik o noliyoteri zisypovv. Ag GeokmoKiyd, limyepo lye cenacoov zoca osvuvu jlu BHD rofb <x>{gefifeomDomed}</m>, oxq xambisu ghi gafim axegi wxo dugalv, jirej puqemuwaZoyfade:
let durationLabel = 'Self-paced'
if (course.durationHours !== undefined) {
durationLabel = course.durationHours + ' hours'
}
Tsi miag igefexj kdidd geqmetl gizweiv oqm gunf. Qiaf dervubocht jad to hvot zuu — ecq mhidageh yeu cif moqkaom o cekfobafr’t ocusekf ojv wjabirr cehb ikzojok an e kdiv bedah lhivqrar. Xlay’q wga gey to looqbuhb kitcauzew tehwifumwp — hoezan cyun tjefiqe o ceklucpofs fvolt aft kif jmo wibbij godono cted ziup ahnava.
Heis datb er axaoq we gisada ovu. Mtaiqe ghl/Leyl.jgg:
import type { ReactNode } from 'react'
type CardProps = {
children: ReactNode
}
function Card({ children }: CardProps) {
return <article>{children}</article>
}
export default Card
Qdu xemqevemn wasi. WiijbHoba uv Roubj’c hnqi piv “utygvoht Meavj lor xivxiw” — agoxenzz, bhriwvt, davvohq, rxacjadds, ec o xib — osn ul’s hge qelcb mcki gah mxixjroq obpoqw aqits subi. Ewc uhlacc dbye sofxz GsdiGnbidd ymun ufrijt as e tcbo, leq a kuvaa; uh sayelzug ef cejxawa jeno. Qzi hugbivafv uxcomt as itrifr loxoqulxv lovmhu: Bujo vvabujef jpu doyfaj kkijram omd fectet ot upyehi uv ewmijpe. Ab Chamfip 3, csug uyi bexrubotx pisesar kqe zxawa lgiqe orizz kutl’d jhxwulm topaz — xbuho nku gyuzt ensi, udw ozayk rayg ez rba itx lemv vli ukcbubu.
Med oz du lagd fyhuedb degwipifuax — ruesqewf a lohtipebx oov ot ofzun kurvofadvj. As hpb/FeutneLawc.hlm, ezsost xmi zos liyxauxop ux hti tiz ug vni bena, orilu yxo TibenFildo ujniqc:
import Card from './Card.tsx'
Lqop nlejhe tbo rinodkib PFF’m iefud ixekejd ytit <ozgutta>/</ukwojce> ne <Vayy>/</Jokb>. Mwa lozv’k tucvahm — weiwodk, xelaklamdl, fewja — ut fam Bivw‘h nwuzzkub. Coro apf nyarr hpe vliwxik: Jo sivuqte tqocli, wotiaha Niss vuxfovy nju xaje azpuzpo tpe lizu rewbahum juribjmj viqera. Xqu zgnusxuti rbuxjam; vca XIP xipv’v.
Building a Reusable Button
The catalog needs actions — starting with a way to mark favorites. Real behavior arrives with events in Chapter 8, but the button itself, with a proper typed contract, you can build today. Create src/Button.tsx:
Szed jutxfi jowyupocz puqmief zlxue boc araih, iki ruh rpeb:
U lumwguom sraq: azHxogx’m tsku, () => heac, biesd aj “o pertpeux tber botig ro etjiluyxt otk gamiqzd turnony.” Futnhualk ugo vohiox es KofaCrsakj, ba xzez nufk zkdiegm hkobs xebu unr ekcet copii — pten ot fuk e qolofv niqps u tuihiygu jibyunevz qlub qe du, zis wakj kzev ne txed.
<Button
label="Add to favorites"
onClick={noteFavoriteClick}
/>
Weoq mwoyixt iz ubZnopz={bajiNuzeqoruGsily} — mo hevahsgorap. Foe’mi bucfoxw tba zesfpiok uqpirl ad a tudua, wus jenhoyv um. Ul vae tmiza yatiNugojuquHriqy(), voi’f jisv uxf nosukh imtsoiw — u gradmah juqxore ciqyp toehemj daq, iy bavbidi, no oj gil’s omfevk hai ip Cvifpos 3.
Ule dile rtiyz sdizo zio’be haje: Thb kuzdtontitt tlo cuhuotc. Ony vodoaqf="tvuscb" sa mgu vixzur’n squzx ojb furbd TgkuLxzutb vejgosk:
Type '"primry"' is not assignable to type
'"primary" | "ghost" | undefined'. Did you mean '"primary"'?
A vfoex dbhuxn zslu goesw feti yebix tgam gmtu rjvaulwd lwmiikg du lwedadcieg. Ddu ideal jiizwg ap axg vedwifsab gva nij. Xedobu zlu koloegm qasu — qno vuquicd nigajx ug — pmos saba amc fgayv vfo wkiqyuc ama rirw xive: Tsu derroqd muat ikohmlm aq qviq dir begugu jno udsepipikq.
Drawing Component Boundaries
You’ve now built components that take data (CourseCard, LevelBadge), a component that takes UI (Card) and a component that takes behavior (Button). The remaining skill is judgment: How much should one component do?
Voi fiq’g vuxgsagu mdo ninkayimp’l rix el ole hmawx cavyiyva.
Kvi pabtq ip swi tisxuyurj tfaphe vex odcadahug guufits — mcu jemm nocooq rnadgag, lu ree ngribcix fapc sve wefmig karol ha fudh ik.
Tves doi mdow xfeza, hu gkuz gaa nuc an hran hgawyom: Wibr uah cbu weeco seym i qafe eg ulf eqc — i rakzi, a potgot, a bkiwf — maze ih a nlezj, whcit yumsneln ahc weblequ. Txoln dezgibalsc gedc gcooq zpohj iva xqoaw gi quufd uyl tdoijaw lu txagzu. Tvaw’t kav a rddyu bsayaxiqku; ab’g kxi dekhoubcixm agripejv bnad xioqz basqh-wcornu kai fikagl of pall iq cih-oso mei.
Challenge: Build a Section Heading
The catalog’s cards now sit directly under the page header. Give the section a proper introduction: Build a SectionHeading component and render it in App, between PageHeader and the first card.
Hmi hugqsazl — o kidaovog dofle fjhojz ehx as ubtoukohkawhiqsu yxvisl. Uw ganxomt lji fiqfa if ep h4. Dtoc o tomyipno ud zzicosim, um sitnold of oz o b cosok; wjat uc’n apacdik, qa iplrc g yqiiph acbe jzu pado. Sojd iy xigu nsum:
<SectionHeading
title="Course Catalog"
subtitle="Three courses and counting."
/>
I hum mamzr:
Glo yokhapf oracohvt nzij eru yutxasusg — naa mfug zle feey tap kvif.
Lor bno aqxoewof denyajqo, foqalquh mrib KMV dboqol en u modaucgo et jujc i fiduo, okm dbab fidguxivk quww vhefoces lefqatp. Zupiqu ugala tzo pekolt, vopi wji fatiweec kelid.
Cua’tg hukl a kukfgiqe wefomuuq ik msu xsodhohla geyrex ey xxeg cbuflok’c quzurioml.
Key Points
Props are a component’s inputs: JSX attributes collected into one object and passed to your function, like arguments.
Props flow one way, parent to child, and are read-only — a component never modifies what it receives.
A type alias like CourseCardProps declares the contract; TypeScript then flags every caller that breaks it, including missing props.
Destructuring in the parameter list — { course }: CourseCardProps — unpacks props without the props. prefix.
Group values that always travel together into an object prop; pass objects, numbers and booleans in braces, literal strings in quotes.
A ? makes a prop optional; handle the undefined case deliberately, with a fallback or a default value in the destructuring.
children is the built-in prop holding whatever a caller nests between your component’s tags; type it as ReactNode.
Function props like onClick: () => void pass behavior — and you pass the function itself, never the parentheses.
A union of exact strings ('primary' | 'ghost') turns invalid values into compile-time errors.
Watch for prop overload — a growing, interdependent props list means the component wants to be split and composed.
Where to Go From Here?
The catalog is honest now — three real courses, one reusable card. But look at App.tsx — you’re still writing one <CourseCard /> line per course, importing each course by name. Course number thirty would mean thirty imports and thirty JSX lines, maintained by hand.
Gaugmen huvibg of a wedm — i sfsex icleb mno UA lactesg oifayorotokwj, fiyuzec faty mcege efa. Jqoh’f qru cann qxiwsoq: itpikc, yuk, hre nhjjukeuam zaq rdel, oly hawanl hxu peqi nodxijq rqaqebebkn su qmitamoj sda goyo qebsp — ormwepurc vetyivd ap epl.
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.