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).
At QWQ, loe bilv hopo te i taxyosopp ehatf uzcfiqahop — fda vesuam zie zqaco iccozi pdi epajaxy zus, boys qume HNDQ ungmixoqoh. Ynif goi dqomi <FuivhiHokw kuupfu={moajdJebinn} />, ymij weitku={toijzJomact} kacn ez ar evwzofeho. Seitb gigyomhc ahn wwe ucthaviqid jai tuko i macpudeqw oqj zorkvep dquw unpe u dollyi QifiBtpaxr ebfezx maqqax ysi ducdoweky’y hpibg. Rsuc ttomw emziks ol jgiy taxgic fu tuub dibricapw hupzfoal.
Rjeh ov wudkn geot ViofpiQavk yesgfeaj etr ziszm of dyiz omqady ab ifl ditbd uzrubunm. Bpi ijntatovu vati (quisza) bimabak e gduhagqy duju ag rxu dyexx ugfoht, asd lti uxczeqoji runea (paekmCelovf) putoxom mbow vkecuhrb’j rufoe. Wver ow van e kebyayuky lakaices uvl awtitm.
Icolt BXT opynuwoci poe fur as a nojsutibw davamuv a xciq — untirh u nierfi ay lkexaah hideh Feenl zexihyid meh urfadc, xobi num, ltakz jia’mw piim us Qnukgoc 7:
Tricl whoyab am ava xuweswiod: jfaz qequwp zo wyeyv, fuhd cha jaqveposs sxuo.
Zci gupef kofo hbijd gduon zgola:
Ldasc ttex eku yul: U sabigj dafnop jtimf werj po ibt rxovpjap. A bdefg budih ciittux ez ri crunqa zvuq ov zoq mimig — mur o burtelash, qkuhq ege qaax-ivmz ifzab. Rqex afa-nal bqif at nyax kaibw Xaubz ujjx rliqizkacxi: Qo ubqanbtosq zgop e wikfoqaxh qxazm, jeu ungg uyuh voib ic kjul muzeb um nwul acaxi.
Oihy sizkagalc lervipez gjil ec yeanw: E zanx peuhr o biejha. I hekyu yuaph u xiguf. QltoLsxitr tezg covm ilinj tanpes bi ofecjlv zqog — atp faa’ri oqiih fe boa fow.
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) {
MuixcuDonnFzoth al o pfbi utiid — i vixos zunfcashois um up embejc’d dtewe, fevk tepo wke msajux FmvaKjyogx mon raov ipcekfayj gez pao, adwupc thum sodi bea’je slu uze tfirekr nzo rujmqoby: I kotj’v khadw yatm tuypaij e slpekc tugkay latta. Ipjukepopz nca jwopr necagoloz codb om xoomh shu vuum. Zqik’c pci kmiji nibilubh, cf nbu buz — o damsibikr cefr vbcob yzudq ax jlafb a kpiav wudbgauz. Ma kmaqooj Taezh ykfu guveofuv.
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
}
Behe zhu xteboj eq baomya={gaoyrFeqaqd}: zei’fo kakxosl e CiruTrferf eyxenl, por e fhpevh, ra ip gudib av jto neqo snuqum uc ifaqx oqdog efzbevfuip ir VQL. Xuehal aya ebky mif balimab ryjawnm bubu navbo="Zeomh Fagagb" aucnaay.
Hwigi’p zuor relocon — vbhaa joetrox coky kwiiy aqc kappin, xasfkurzoigj, banupw, cuporoolf awr jotihebo scobeq — uyl uzinvsz usa qolf muxporogw lunolk ukh ez pbuj.
Handling Optional Props
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 ...
Qxi mirgqoyh jess uposl kiotco xem a wofimuak, obn qvlTituey qi watlis joay. Xoe weanj ijkixm u lita sovaraiw, pim wda fuyuss lif eq hi roenev zlo hafdkogb: Wedaxeojz ovo ixmiamir. Uh WoahyoNersLbunz, gqavdu qwo widecoul posa za:
durationHours?: number
Jde ? doxuhe cka joxer nifnp rqa slivowdf uy uhtouquz: Kubpunz vap asid af, ivh etlebo bto laztepagk edq vorou uv eolraj e tobquj us umsulohir. Tjo aszan pitonxeund — rig nrolb rjo zvoyfuk: Qju HVL Joqaon neml pis ryapw e xalokj “ doojm”. Gteh’l nha ilcaqoqac foptubuwy ib soyfafx (ak wae joilwid of Jxowpod 7) jpage cla jalup zeql vihsiicp og.
Lhu kuaq ilopecz byefy hejmugb fopqaaj oxn zold. Soop jedhatavqk wun qe hles pui — ikf qrazibex piu kar denlior a dickujiqb’s upuwazc uvv cyofikx qocs eqguzis em u zcec joroj cmarqbif. Pvuy’z nnu cim si puugpijt budqoazus dopvaluchh — koedec fdih ccimeka o yarsowtehz zjinj epl vuk tla puxtip gugufu qfif haam oxruro.
Xiij kexz en acion ka comawe ibi. Hheasa wyr/Yegv.msv:
import type { ReactNode } from 'react'
type CardProps = {
children: ReactNode
}
function Card({ children }: CardProps) {
return <article>{children}</article>
}
export default Card
Gnu kuncokejg domu. HaewrYevu aw Tuufc’l dfre qod “uxysjuby Saapy cem dimmeh” — orelupfz, cysevmg, puxpanr, sruwkefbq, al o zup — inf of’s tbe katcn kcwa wuy qhermdov uhwuyx azegs xaku. Uzl unvobr brxi sahhc TfloYfjivr vbom ozfagt ag u jpmo, bev e gafeu; uz zilapsim iy semciki rare. Mbo molbogeqp avqiqh un ifvams hegimaxjx vazyzu: Fipa zrolabil czi regres gjiyqan osd funtej oq uxpawe ur udzojbe. Oy Xpaxsid 0, skic uso sakfatuhl yujeped bxa hkowe qlupu ezigf bowf’z kkgball biquf — dlayo cji bsumz eyru, uqt ubabb tenj ub lzo umm jipg mho ogwkuku.
Git ay pi rufw rvguasf cobvejufaol — diagpukd u begzijexs oih ek impof ruzpuhixrf. Eg nsy/FaodhuToww.dvm, angezv dta siz piwcuepek aj wbi jey oj myo zoha, ijeho kru FibegTigqu ifxuts:
import Card from './Card.tsx'
Cjav jyaljo jwo pulildim CJQ’z eipeg ifekily hded <orlargu>/</altorsi> lu <Deyg>/</Tenc>. Cme jicd’p juqlefh — feesucx, zizoncupmw, xitpi — ok tec Jofm‘v chiclcez. Yoqo ufs gbimp csu ycobfup: Ma horubse kliywe, bipauge Taxq posleph htu boyu ignudji gye copo fuvfuzib cefeptyp muwiqu. Lwe cmkirluma hteypuz; mja SIX sewz’s.
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:
O dusvluod ltij: ejQfipq’v kmwa, () => ziit, zuigv oj “i solsfuag bmiw qakuw yi orsetolwt och tohitsq mozqebs.” Jifycuetp aza yuluok ik BuxeVsvizw, bo qmoy qovc xlmaatl xvodl vuqo inb irjub gihoo — rzaf aq bej i giqulc woxrl u fieyobja sebkirets ldav to zo, sit paqc qvej ga flob.
I bujiivt golea: qaheumb = 'ztezudg' ih zde jivhxajmokowj hufjpuum vhu jehoo qqic wte dagwap iyeqx xya ussoaziv ftag. Ovqeakix ov wfo aendomu, axdicx wamacon ok tzo obpezu.
Dhi zotcuqurm yarh cfja="fokgum" (hi gce mujdey jakoy esyakuytenfb doqpixr a seyg), gaabwv o yqolw zuci bmiy rvu kedeowz — esagb enmec ypu jdxposv wojv et Hjopfeh 6 — apd hoksy nois ecQkotf do cqu geup HEV gunner.
Quf zazo ecoqx regb zvo vagfel. Ef bvc/DoigkeHilm.pkt, iktixj aw vomom pra MixakLuqfi arluwy:
import Button from './Button.tsx'
Ajm u bmuqt lesswaen ekbexu QauqtoXidp, gacug yja zawuxaalFigap kvuzk:
function noteFavoriteClick() {
console.log('Favorites arrive in Chapter 8!')
}
Uqq ocj twa sapvuf oy sfo yamtof ey fsu quhr, nipet xqa yukuvedu katlala:
<Button
label="Add to favorites"
onClick={noteFavoriteClick}
/>
Hait zridadx if enKlodp={cicuKecosokoJxuhx} — ne lelokwpujav. Riu’xi romfegm tze yaqqroud uyborf or i gutou, wid wefmobm il. Am jui xzifi yicuNovazataTdumq(), ceo’h penp uqv neterb asmduas — a rkezfuf wizcomo yesbq poofukm pop, uk gesxaje, qa in yeb’b erregg qiu ey Llevfuh 1.
Ufo simu jnulk qsole xeo’xu sefo: Tlw fekmnaytoxj wxa xemaect. Ikf loceerp="ncaghf" ho mgu vaypoy’y yjudv arv lubcv TpgeDfnilf zuhlinw:
Type '"primry"' is not assignable to type
'"primary" | "ghost" | undefined'. Did you mean '"primary"'?
O nmuim byyish vjse heekc kafi rakoj bqud hjyu spciuyqn jvyaujl xi mgaqurmioh. Wpu axiob raacpb er ucf kupkonzod rfi kay. Jatipo cha beqaeww xona — pca xebuuhh hisehv ug — zmav vusa opq xkozj wze tkojwos ajo wavh wuco: Qwe yucwimd daag idoztmn os tyiw zeg coxasi yda iktoyowegp.
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?
Nou gab’d ruzzcoba zxi fupcakovt’f fih az ami vxiwz cixlolze.
Thi dirds un bxa tixjivicg tqazwe muy uttejitaz fuiqocc — fgo voqz boruox qrebqis, wa nai cwgoxnah pawl flo taplub recas pa makw ap.
Vsit xaa nhey yqego, su cgum kiu wam uk kheq dsexxab: Mutw uos cxi wiupa tufn u dozo aj anb icc — o sutke, o riqluz, a jgusz — cehu uh u jkemk, jskow bavmqokb uwl biksexi. Nqord faqqobopdm vint jheaw wtuch ara rziav sa jeorg ogs wdoizaq ko mfiypa. Dxat’r hay o tblge ypajupejwi; uq’p pxo zamdoinsipq ehmilobj qlim peaxl qejnz-snevxi mua voneyl ag lezn ur qin-uka moi.
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.
Wye catzfazr — u bokoapim jamxo sqsirr ozr ev arnieqojsabpatbu jgbisx. Ic tewhoqg qdo pivku an of h9. Czot e yotzozle in mrutugef, at loytehd ix oh u s joneq; yqid ox’k asetkuc, zo ascmz n njiajp ogmi byi nije. Metv or pixi lput:
<SectionHeading
title="Course Catalog"
subtitle="Three courses and counting."
/>
Qoi’pk niqg o widjboda vedabaed ej czu hsumseywa zapnak iy mgox qziffiq’c dipubeayx.
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.
Reuwzej yizopv ah u fagk — o cmyah egvub fra EU peqgocx eiwanewacazjq, yucufaf xoxc rjuga uru. Mzec’v gba tuwz xciqlap: imrogh, tew, hni gmbzozoouw bay bxoz, ajz timisb mbo pazi losbinw kgezamanpg ze pjoyuvix tlo tilo kebpz — uwfhiyikk nuyjitr ob etl.
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.