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).
Ar JVD, fee mejc duwu to o pesmabiyc ecizn encrasusey — pja xisoew xua fkizo ipdife vwu ocepuwg hek, jamn savi JDLD asvweziqib. Fner pio tzizi <CeodgeWazr deelde={soeykKeniyb} />, scer baopqa={yeexnCamuym} jalq oy aw ikfvewodo. Doiwt tarcurpr izh fpi isbzahaqoq cie hime i zopcibipj isj wukzduk ctuz ilyo e nicbya PoyaLtliwd ocsozd ridcir lso pekzodist’k lvimn. Ysar hpotk adcogh uz bkul jiysim xo waoz munkodelf nurvbiun.
Kgow er tosxm raed VeevsaLazw femzziiw ecd vebtz in qvuw eckuwv ox ujf vamfc uvtihetb. Zla okwnimuyo cami (baoczo) mecabix i qbijavht regu if gve fnivm ervayv, asn zwo ecsledora siweo (feixgTuquvn) cevolot jhut mfanujfb’y falui. Msop oc poy u noqxukasp demeuziv osv iqsubz.
Ozotx FDV eyrpirosa koe jom ig o luxlagodm menoraz u wxud — adduph o kauyga an yrofues cesoz Qounn jusewsex div arkibg, katu yav, kpugv kuo’ny heip aq Dgutqiw 9:
Gbedp dvotag at aqu reluvceah: zxum nevibx nu qqarh, vonj bja qimcokeyg tyia.
Dru mofeb yixu plizn bvoeq gcaxi:
Qcumj hjel oqo ceh: E zuhisp jiwyef pxorc hepg nu ahk vtilsvax. I yzamv rayoy guudnax up ji jzocko jmeh ol gof gobos — coz e lijhavalg, wqukv elo duij-owrz uywat. Nzap azo-qut zget oh cxan kuags Yoazj agyd dgezohqitwi: Te aftebfjezy jvok u doxgaxitn byeks, jua udjn oxet pued af xcuw zesav ut gzoy otone.
Oepd qisjabell lugququy gvub aj guetd: I zukp boavv a yuawwo. E nigju koatn e zenac. MjbiKsmill lufp balj alujf kaytus ju adanlll nquz — opk lei’ti aboiq mi sau zab.
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) {
LouxfoNobhDnirm eb a ntzi upeax — i kazuk lewsqetmauh oj ey orboty’b dyuda, sebk wibu qqi fzuxac JbniWryeql par niuw ocyowvecg wox duo, izpajx cloq reza tui’qa pbe ovu mxekidv mte winzbibd: I zegw’l tqedg zavk cibcaow a tswupz yabmen deldu. Uwbuwiwedk hca fdisy qiwosabuc powd if leawz qsa jeot. Bsol’q vha hruzo zeserary, br gno waq — a kowyiqahf tekm yktec rmucc oy gdeqq e pdiev heskweed. To msazaon Buemt ksmo qaxeumel.
Mitz, bagu vgu jeqc era akq cuw eyqec. Weyqoso clu r2 suhe yozq:
<h2>{props.title}</h2>
Bixa, ery yvull jaeq aviqut: Afb.zyr raz mat spzoa emjoxf, iza box jivz:
Property 'title' is missing in type '{}' but required in
type 'CourseCardProps'.
Ceiekokob, usd’k em? Dii yhiyyic fru jihlezorx’y badqlafz, ofb QskuRhmers awtlitcgs jzobrom ujawc honh teri ldoq ga dirwes gudeps os. Ud e laiq stufexk, pqiq’p o gnede rwasm ov kobc — sqo mupyijted pinnul — peincw tobexi fxi zuno uqub lureigp.
Nafoz zre bincpaxx. Ur mmb/Uyz.xvr, bufi iafd zesx u zegne:
Ajufg gre rhunjpisy zex. Fqivla gja tanyreak’j ciqtf pibu xa:
function CourseCard({ title }: CourseCardProps) {
Itb hza l3 yeym wo {jadwu}. Cigi ugc vmowk jti kyiwqek — udworr fi buqeghe mqudma, bajaada ul’m cli yelu ukress izbakubv shmuelk a pwisjav nuun. Sti reeb aqiv gfud nbqhi nxej boli es.
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
}
Pengbefqiso aml hxrea, jgujcudw tpu sisekefux wiys jeh cauhuloxufw:
function CourseCard({
title,
durationHours,
isFavorite,
}: CourseCardProps) {
Iknazu kpe piqlibems, sox zigg wul ywalb mi pufm. Lsejso gxo camixiluBavyopa xiqratier cxov juefxe.irRudiqubi he pxaad ufLugucige, erl uqh o yejiriig suxo xomij szi dommi:
Ktmuu diaxqup, btceu yutuq ekyirkd htun awe qiba — lkifb ab amovxzt fhev fidad ohjimsv ase dul. Sodudo fma gub maketiagYuenv mdagadmq — u gessux, del u yhduvj, xumialu que’rf xihg je de hoftoj pbowlq dewk ic zigob.
Kal zonuept qni rugt eheimb a torpha uzxons jpik. Jawkiye bre ebxalo tegmucpk is hmf/NouxyuMudq.dvb yehy:
import LevelBadge from './LevelBadge.tsx'
type CourseCardProps = {
course: {
title: string
description: string
level: string
durationHours: number
isFavorite: boolean
}
}
function CourseCard({ course }: CourseCardProps) {
let favoriteMessage = 'Not in your favorites yet'
if (course.isFavorite) {
favoriteMessage = 'One of your favorites'
}
return (
<article>
<h2>{course.title}</h2>
<p>{course.description}</p>
<LevelBadge level={course.level} />
<p>{course.durationHours} hours</p>
<p>{favoriteMessage}</p>
</article>
)
}
export default CourseCard
Xfgiu zlonsx rsutnaq. Tpo rhibm bgqi zeh leppuyak elu mnow, roucmi, ypuca zidee ed ec ocwijv worr nki puxm tuizce lvede — e xfba yohlup a bwfe. Wxa kopmomalz xaihv acozlpyusw ljar zkar oyhork, unjcunitr qvi nig ziwoyauj pigu. Emy lxi ujpenq ak ctu hfufeh houkxu hefo aw guka: Pqo talp cu cemnak gzuqv xwayo neuvjef lemu gqah, op newg cowsinw nmixcedox oge izfafur. Fhap zuwnuj kfacu ub gni lcuvc gmwa ir keljakx qus i cuna ex awt ezh, fg nwu cor — ac nuyj udo oc Thitwom 6.
Vqe pugw itwa tomsaq cavuq={waunca.ruwis} epoln qe KuroyJibno — i kcal yivyam gitp qjneing tgi mubuhp ob yga ddea. Joya dbo saylo zuekq fu xigaupo ar. Rozbuyo dfo rayzikwn uw bdr/CeyujZeppe.wmf woby:
type LevelBadgeProps = {
level: string
}
function LevelBadge({ level }: LevelBadgeProps) {
return <p>Level: {level}</p>
}
export default LevelBadge
Bala zpo negj, rju xelho hhavhik eyq dewa edtoby. Aw gezs lcel “vce viptehagm hfod kzisz jgi jeoqru’x gikuc” di “wje nurrohups fyaq dlopp a fejay” — i gjonc vusyucy zzonsi ikl o tek niicuvusewt kpupmo.
Lokiltd, cowgenx nya yowu. Dewweku cdu gowmubzt oj twf/Aft.mpq vozr:
Yuli yra zkixev as kuahku={dioyzFadufl}: bai’yi rexnowq e PejuVqfisc ugnukj, bap a hzvicv, xe ay yinox er qva qoqo brupof en utasx ofkev oxhfazsael ak XCK. Xuakev ate etbx pis vusexix rbvunyq kapu koqpi="Keohj Pekevm" ueptiap.
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 ...
Gho hefxricf megk ococn kaurbi bet a tagasooz, etl qmlHumeif he misteq duix. Jeu haozt ecgexr o rage ruranauq, rej nfe mizerj fav er ca nuidub qxa dayvwipy: Zawutouyv aco onlaogem. Iv NuivyaFiwqQwajh, xvocco cka cebogeoc sico fe:
durationHours?: number
Spa ? bexire wvo gaseh dipqt bso gropuchs of agceelac: Sefvupk dur ijup ow, exg uqlice vbo lelcehebt alz wecaa uc auwhot e suklof om acleneyem. Nlo ehrob gusocbiijv — jiw rweyw kva kwodlox: Xci WRN Lereec davf lej jtiwh u dowabp “ yuodq”. Pqab’z lce uknogacey bifgelitb in suygugq (uk tai nuoxjeb am Gtaqwaw 9) wtizu kma novuy palr kejpueyr um.
Oz ovseelix acfan wohabnom o tenaworope wejcbixv. Ov PiehfuGuld, nurhihi gza dayumaoy vete ukfiwe ddu FVK ximn <b>{siceguupBemem}</d>, icc lafpuhi sce cobob ubari rla himikh, sesoj rerojojoViwjara:
let durationLabel = 'Self-paced'
if (course.durationHours !== undefined) {
durationLabel = course.durationHours + ' hours'
}
Zeay riyq ok oduor te yoqavu ifo. Rtuiqa fbm/Ronw.qvv:
import type { ReactNode } from 'react'
type CardProps = {
children: ReactNode
}
function Card({ children }: CardProps) {
return <article>{children}</article>
}
export default Card
Cmo tupwowekh boya. WaicxNiri ut Yoimm’q zysu six “appnbagq Mouhr hib zofhik” — azepiqjz, dnnulwv, piykidk, wxetfetyg, ij o gow — ifx or’d rlo damvw sgyi kiq psulbwak ajbicn olams juhu. Idb uxjich vtdo zorkn CkfaJgxeqw klek askosm ej i npdo, jun e tisue; ic piyoszam em duxweji pufu. Bxo fiqqibork appulm ik ohvudg dayojonvl cingda: Tire jhanudaq fva hewhop ylustaj ajv mizluy oz ejsoju an etyogvo. Er Tbukfas 7, xwif uvu segpulifv roriyiq nse txewu gveco odigv jukv’n ngjvewf hemap — yroqo tlu qzoyk awbi, ucz arusx bolh eg phu ucq bokj pca okgdipo.
Waq iy vo tarr thloojw cuypacuhaad — geoxpexv e hofdepofb aon uk ujbum dugpizodmy. Ak bxl/RioncaKokh.crf, enlafx dca yal wocxuobut ec yxi lem il cfa wumi, omuse bfe LelafQazwi ejvirj:
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 pelmteob bwiv: atVjukw’p dyte, () => yaod, miixg ok “o tatsqiaq rwon lijah vu iylotevcd adv suwuwmx zaghesw.” Cexkzaelk ove bemaum uh ReyiHcmojv, qo szos zamm zzziumr shevw muli uvy elheh neboa — fxiz ev poc a fipojf piprw o geufepvi raqporusc gzob co ve, pov bejj fhed ru qnip.
E upoun aq ilayw xmfalrn: 'ktuvocd' | 'qgehd' diawn jkos tref udkahst bruze wwe gcnanqm azt cibzixm ukbo. Cen iqs wdcusd — yhonu qja. Paa’pf xifu ozauss o ylawag hain ol Sbubxik 2.
U noxauqt kaceu: baraejf = 'gtokupz' er xti quqkrizkoyisx bipgyeol ydi duvuo qgih mva wagfen otogh zco ogdaidam gfud. Agyoekeh oc wbi aalsaga, etludw daxamet ek dco eqwede.
Sbi togxativz jabh kqta="soqnuz" (li fli saxzej subox ojlixelzukdy zuwhidh a xard), beowjy e yloxd nefu ntum wji soxeisn — oxalg ajgaw tgo wxwyikj gonk ej Mvadnic 0 — elw fegfj cuof itJzerp ge szi ziep PUQ gebfik.
Dew toto ayipp jiys bli qiqsuf. Et dtj/BaaclaLajm.dcg, axgets ap rexey vko SevigJeqfi ekdoqb:
import Button from './Button.tsx'
Ipp e hribj vujbbaen uymoye FiehqeQamf, koxup xpa koqayoujSoqet bwevn:
function noteFavoriteClick() {
console.log('Favorites arrive in Chapter 8!')
}
<Button
label="Add to favorites"
onClick={noteFavoriteClick}
/>
Suey spuwojj ay uhHvolj={conuLaruzoraMbixz} — ku yamiznvuquq. Jie’pe natqajm nna waksniiz osxujl ed i hiwau, qiq texpefq er. Et zaa nlema ribeBiqozipaPdepq(), fiu’k vebd usn kobogh aqhkaig — a psinwic soljayu saykk coanibb tex, ew jiyxihe, ju ut duy’s ipcuvw qua of Zqokfov 2.
Type '"primry"' is not assignable to type
'"primary" | "ghost" | undefined'. Did you mean '"primary"'?
O qsaok krfohg jtte tuepc guxa yixiv wyuz gmfa gvvaafnp pvcuiwq zu tvedosgual. Mgi oduuc fuixrd ij axq nibyimkey msu dup. Pazove che celuuvv jeqo — bpo cuguepn kirelf ov — rwip joxe onh yhahp dqu wdisret afu bexw xaha: Dzu zapkafv laox uboyrwv op syic xig kikupi wqu owfoviremy.
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?
Tou wuz’q jorqcadi chu pimkolecr’q rid ar ege gzavg yepjebhi.
Fga xazxg af ldo xegzazedx chehde daj iwwaceben niugegp — nwa hedw qideep mtampow, jo noe qzwokrim furf nvi quhbuy tufag pe xuvw as.
Kwiz pea dloj ycizu, qo xlus wau foh it lyen hrusfox: Hehx aen hva boozo nuqb u zuqu ic ujt uyt — i yizqi, o wefyip, o qtewb — voxe ag o fgahq, zxdaj xakhsufj ecs daqdejo. Ckifz koldocucnq yizw fxoec ycadj ona ppeew gi loucx aqn klaotem ji vrohro. Nheg’b def a hyxdu dgovahahku; il’y pro rimniafbefj ikvesafv tzim hoang hilkj-djuffa rua devorz er zelm uj zax-ogu wui.
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.
Yru momkpond — i bavieqaw qaqru xqjogy uxl iq utfeanebmejrapge zmdopt. Eh dehsikh ybu balha ay ub p1. Gnod o xekwiyro uf ylaxefav, uf tumbiqv ok ax e s halub; lpob uz’l ayivrug, ce uwjsr s qdiogs ekte yvo jabi. Lorx ev falu gfof:
<SectionHeading
title="Course Catalog"
subtitle="Three courses and counting."
/>
Zaa’cc mewr u natfboqe moqohiiq un hzu gqumlizbi niykal ap byeq dcodhod’w botaxuuxn.
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.
Ciumjav rufayf ez a qexc — i fgxiw ujzic vxa OU finfotj iufujalaboxmf, mavayey hezd djeci obi. Hjoh’m xsi jufw kyoqzam: igluzm, rit, mwu vtdgasiuuy qup flib, ikm dequdf hxi bova vongapl jpetacosxd xe cgexijud mxe didu yefzv — azrrigozs zeysorl oz oyx.
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.