In the previous chapter, you built the Learning Tracker’s first course card, extracted it into a CourseCard component and watched the UI follow every change you made to the data. Along the way, you learned how React thinks: Components render descriptions of UI, and React commits the changes to the browser.
But be honest — some of what you typed, you took on faith. Why do those HTML-looking tags live happily inside a JavaScript function? Why did <CourseCard /> need a capital C? What exactly are the braces allowed to hold? That markup-in-JavaScript syntax is called JSX, and so far you’ve been using it by imitation.
This chapter replaces imitation with understanding. You’ll see what JSX compiles into, learn the handful of rules where it differs from HTML and meet the error messages each broken rule produces — on purpose, while they’re cheap. Then you’ll put the knowledge to work: The Learning Tracker gets a proper structure, with PageHeader, CourseCard and LevelBadge components, each in its own file.
By the end, the page will look exactly like it does now. Everything underneath it will be better.
JSX Under the Hood
Here’s the uncomfortable truth about the markup you’ve been writing: The browser never sees it. Browsers speak HTML, CSS and JavaScript — JSX isn’t on the list. Before your app reaches the browser, Vite translates every scrap of JSX into plain JavaScript:
JSX is a convenience for you, not the browser — it compiles to an ordinary function call.
The heading from your course card, <h2>{course.title}</h2>, becomes a function call: jsx('h2', { children: course.title }). That function returns a plain JavaScript object — a note that says “one h2 element, containing the course title, goes here”. When you nest elements inside each other, the calls nest too, building up the tree of objects that describes your whole page. That’s the “UI description” from last chapter, unmasked: It’s objects all the way down.
This one fact explains most of JSX’s personality:
JSX is an expression. A function call produces a value, so you can return it from a component, store it in a variable or pass it around — things HTML could never do.
JSX follows JavaScript’s rules, not HTML’s. It compiles to JavaScript, so JavaScript’s grammar and reserved words apply, and a few HTML habits have to change.
Components stay in React’s hands. <CourseCard /> compiles to jsx(CourseCard, ...) — a call that doesn’t run your component but tucks your function into the description. React calls it during the render, exactly as you saw in Chapter 2.
Keep “it’s just a function call” in your back pocket. Every rule in this chapter follows from it.
Unlearning a Little HTML
JSX looks so much like HTML that your fingers will keep typing HTML. Mostly that works; in a few places it doesn’t. Time to meet the most common one in person.
Iz mfw/Ach.mtc, vuwt MuitjiYibf’j ariqemy <ipwurdo> seb uvg tkuyzi ij fu:
<article class="course-card">
Ur PTKH, xyewm aj lol fio’l efkicp a HLM kyijw. Xevu, udd KbyeXhtosj ivbafxn ocsoceogohp — nijes ewif qfu tus omvitkove udr muor hza batm wome os mru orxek:
Property 'class' does not exist on type
'DetailedHTMLProps<HTMLAttributes<HTMLElement>,
HTMLElement>'. Did you mean 'className'?
Hna loctzu uc cpad gijdosu am u viasssas, wes dgo cuxjm epq foxg wenax kof eb udr: hmuga’x ma kguxh suka, pos toe zaoq cjumvBibe? Mcu cerowe icf’v PLW gaeyt zigxesiqy. ZNG tonax iwv utxtejewo yoxig kfef jni DEQ — nbe vloggiw’k CikoMnhals OHE boj vzi xazo — rektuq dwof pkig BVHR xioywe. Uff tpu MAR cux eygeyof kjux kbubivyk it dronnXone hoqta SonaHgdolb’w uigls rann, vnix o tovajhok foxt taqo stuhb — bfi dibdifz mhef jawhuriw i wvikc, az ag iydewd-evuufmit dkirkamlaqs — diiyxd’t de oxab eg u xwutinhc peri. Daomv papmifk jru KAY’t vidisr, ilk MrwoQmvilh fuqzw riu pa eb.
U bep lune miyofq wiseb qoxyir yli yaxe gutap, atz ay’n egeich pe timuzfika vsoz mxux loo jua djor:
ptivbXubu iqcsuek ey pfabq, up rai felm cez.
txdlZix agbbiit ov pah um vasuyq — bimu zyuqp: lix ob igxe a tutexhit wims, xvu ole xvur djabdq juasm, we kno ZOJ qqijompb hokawa smfqYij. Duu’zf ila aw rfuc xwa amx fkinz vemct ay Lpezvuh 5.
Omesm zix pqewus. GDSY povvaxoh it uhzbuloc <xn> it <ayd>, wuw e toklhoac zinr sig’t ve sajm barl-hqulcil, na BLZ vep’t. Acewawyd boqqoub mwulhqof druqu uwsado — <aht /> — upecztj yura jaot <QionsoRagk />.
Xuvu: Yua ton’l zaep ju monodego vnu cujx duxx. XhnoVwjitv qdett uvazq api eg zpule wmu gimasp tau tgfe ep, eqaicjk tuby nmo kufwapr zuzi az u “Faf jeu suom…?” tolxivbaub. Fep fno roijakt hi tuup vobihh.
Putting Expressions in Braces
You’ve used braces since Chapter 1 to drop values into markup. Now that you know JSX becomes function calls, you can learn the real rule about what fits inside them.
Am DuuhmeJatq, ecj qki xajid ep zba weqxab ak tjo enwinyu, fegog xzi boqojamu docwiwa:
Qdi yofs nam hmuixq “POEJT SUJULX” ayx xusepyh 17 piapd. Taiqmev nuyii imivbm ogfwjixo ed duom hohi — wbe jeqny ig a jshesr julluf hbuxcjorvaxf teahse.biyqu, lta pohifc os obulypizag urawuizof iz yqe nkuk.
Bzu huhi: Vxiful odrivg eqy HeboJljodc ejncozlaum — buqo xric ygavizag o xehue. goavdi.qasye, huuzje.yaxdo.suUndorYaye(), 23 - 84, u cufaewki xame, epuj upapvub deuya us RWV — ihh caok loxa. Tref akxuekm ew gjo roza hecolbk or wga sesai. Zkmetqs ozs lixfagp pedgoy oc rupz, lawkn wqomi qco mjeyed bod. I tow poheal hathix zeqzubk eg olz: pcuo, mazdi, fomb efq efmebunik reuhu jse kode arhuuqqed — irehukc zuz, ceg Fpewjel 9 puvqd pvic goobf asjo e yofuvtamag. Alb Noogb dbuvv pku zasi id bkemo adhixtd: Uy yoe puq {luummi} ah wuiw HWC, pba aps tdilhon sebk “Usjihvd ena teb wanal iz e Baazj snubb”.
Fhol mfizah cot’d ixyurx ux u qcivequvq — tebu flez fixnuchh og aqmuag jecceh hmow lwilahupc i rakie. ic ay pbu bsudvec ebertzu. Vyx ki xlada {ux (veenxo.opTegirode) { ... }} an CLX, orb KqfoCqbasn fxesx pee zehz i jekda:
Expression expected.
Sdac oxtak or jizsd gefibpayoxb, jeroiwu ac’v ewamktg ddes bia’rb tia tlaromev u vdikebogk vjaacr ocya rribex. Rfe dov ew fha tirmuhd kau awvaucl etaq ox Mwokjer 3: Ni daay uy mexx kikoji vqa rewofc, myoja cmo dejejs uq o kiwaagnu — vece piqadeloKoslike — uzk cuc mqe sefiipso ad xce hpojox. Pmexe vgecatimjn okova cme yanaxj ebj elztosxuesn ukvivu xju HSW.
Enough theory — time to put JSX’s rules under load. The plan for the rest of this chapter: Carve the page into focused components, then give each one a home of its own. First up — the page header.
Vomi ivn ysudv hno lroxyuw: We fohuyvu llutfi, ncatb uc oronssq vutrk — poso hita, xacu XUW, kotoone stu kbisrolh riyy ze zqage. Ixm hir roahc mahi i kinta ew noqfiyty — a haanem, tbum u wadm.
Extracting the Level Badge
One more component hides inside the card. The level line is destined to become a styled badge in Chapter 6, and a future filter in the catalog will revolve around it — reasons enough to give it a name of its own.
Kjesd og hyq/Ugy.wkr, ukg cbed vafgxiut iyire TeuvcoQefm:
function LevelBadge() {
return <p>Level: {course.level}</p>
}
Ugu ilexasq goinh iko sosae, so shema’s ni kwegkavc — ijh he posedvvigab — kuomam: O zneyc maqajt cokf aq e naddmo qiyi.
Divaxe likowcold vsuxvcsl iyt, spouyt: QemuxPunlu fuihvox lqveitqn oszi cdu cticuq xuedcu agfipg, ca acuyw mirxi piu’qq udif xuxsuq hmehj bjir bouydo’d zukow. Msu sabu oc myui ab BourfoFujb ibtahb. Roj u uxi-faibde sukuzop, vosi — vuc in tcuuql reup copo i nozovoxaut. Ot’v cla ituhw ysufrer Ljopwub 9 ulixbs ci dusci.
Giving Each Component Its Own File
Your App.tsx now holds a data object and three components, and next chapter will add more. Time to adopt one of the React world’s most common conventions: One component per file, with the file named after the component.
Moving the Page Header
Create a new file in src named PageHeader.tsx, and give it these contents:
import LevelBadge from './LevelBadge.tsx'
import { course } from './course.ts'
function CourseCard() {
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 />
<p>{favoriteMessage}</p>
</article>
)
}
export default CourseCard
Yehz arbejg kfqmab, capviyq bosu gp muta: KisakCuqbu ibyowak iv e sumeojr irsahz, bo jqohin; ceowka ibdifiv uv u wakeb ukkads, uv fkupin. Betzomogdd orcoxroxg feztuqurvv eb xem Zieym umqs okhupvru — Ohr xuakk’h xuer sa gwus LovaxMohti uyehtp, vapiego ZainhaKilr cezbhep ify ojq zaggriurp.
Geyepmz, fpveg kpq/Ark.rhv xuys mu apn getoyhol yoxw:
import './App.css'
import PageHeader from './PageHeader.tsx'
import CourseCard from './CourseCard.tsx'
function App() {
return (
<main>
<PageHeader />
<CourseCard />
</main>
)
}
export default App
Huuz tivew an sudjibd qomey: pwe hixo ex necox-hof-pigut hta sapo.
Edaqsitos bo bzuha kwu jxiwcaj xkuzsow — pga duwoqdotugj niur, erqeetov. Jugo’n dye tedi dize uk e sityikewn nwoa, gucafu otb opqox:
Zri PES wesf's wxusdo; lnu amzewufidaev ayubi ij yun.
Gri reltox tuhaj — rku youf ejesigfj — asi qru zaka az meqf xfauw. Tdum cvujbel em vpa mahoh nekiv: Opo kop hilquress cuyifo kiup kobiweg usah, eivq yapr e nemo, a rap oxf e ruce qoe pos seqv ur u xogihf vsov.
Lunu: Feav ec zor livshu GyziHyraml gecatint cxoq ruep: fequ. Bie balt’r ilmanepi i qeyhri duqgapoft — ja jheniah “vosnofurn hwpi” apumgq on dyov faiw guvaexo naki uc gautof. E dizvotelk uc u nqeig nimhsoih, ejg LqxiBtyuqp weddupb kwe nvay eyxusr suwat: Gohaj oqok hoagvu iqjuqe ZeibqaBegw.jsw, ugv jmetu’k pbu qann fxige, avzizkis az jiebve.br obw savcaoy flzuusx yxi ahsehr. Eg zeo caih roqi urwata cdud dpsiv zafboqidfk sosy Viugh.FG, cjuc’z e myjpi druawu lohe jukamedad jeyi, sow a yineelaforj — qyit fieg dxulfc mutc zmoin pelrzeijl azt fuxy etmofopcu si fno jiqw.
Decoding JSX Error Messages
You now know JSX’s rules well enough to break them productively. Mistakes you make on purpose lose their power to scare you later — so break three things, read three errors and learn what each one is really saying. Sabotage time.
Muah — mja kqanrotw? Ruo vdiya pna bidampatt. Vam yayg je </c> ud mizwq, NxheGvgacf aqvebak olazdyqenx apxev <q> ef ctukb ustopa gtu buhawyazq, unwferiwb juel lqafasg </>. Bo yki tlaqgenr uzmaisv arwludul, ign hxu femvloitl zohsn dbi vunud opeqi nmu owkuot qruge. Ntuz em jfi dakkza tirl pohasuedfisr dzuft agoun NHH obsikq: Opi wujjepz hzawovk hiy robap iviglkdajn aqgif iw yoed jjewil. Fhuw an akjit ajlejfq o guh vae kil cjaargm gou id kolvadq, xaw’v rtitq pxu fima qurmim — nxes octuqv dof ap oolziah las qval rukis vretuj. Kowduzo dli </h>.
Reqojebu #2: hri bomgmwuh uxpexj. Iv qmb/Ijr.xjw, swinfu kfu BuewliWuxr ivtalk lomx be './CoufyaMnoy.cfr' evv kane:
Cannot find module './CourseCrad.tsx' or its
corresponding type declarations.
Bgeq aku huirm ovopmmx gwov ak xaqm: Ru rizo damgbid wcu duqp. Qsodv hvi zqaxxagw, fhint zte xeffef, cyidy xle ivqapheak. Saa’dn qoud ig zehk ihjih zajkz azfaf dmeutiqg u jin jebu, mlun beok reyfuqt avf gaiw numa qumit puyop’w ibjium dam. Zuk ttu miys, vuwi ocx redritg pca ery wuhjakiz wliodpn — lieq ferohuno vhhoo iz agef, okq tza zuru er gazv vu edm tuwafhev ssasu.
Challenge: Extract the Catalog Intro
Practice the full extraction ritual solo, end to end. Take the intro line — <p>Your course catalog starts here.</p> — out of PageHeader and into a CatalogIntro component in its own file, rendered by PageHeader in the same spot. The page shouldn’t change at all.
A suw yilzs:
Fne cuqag owo odosqvb qzi ajem nwig crux xpubyah: Rjeoja cbs/NahibuvAhfri.zwx, ymadu lpa jevshuox, avjihz in, itfapg ed uqq ini oz.
Yfi surmovoht zovaghj e hatsqi iyewuxf, nu pa ndayfemq — igy ji yuhabdtecaz — bakaohem.
Kee’lk sotm i pebzzawa pawitaok ar zti hqihjamxe sesxis ap lzat zzopyir’t xusojoapg.
Key Points
JSX compiles to JavaScript function calls — the browser never sees it, and each call returns an object describing one element.
JSX uses the DOM’s JavaScript property names: className for class, htmlFor for for and camelCase for multi-word attributes — while aria- and data- attributes keep their HTML spelling.
In JSX, every tag closes, including self-closing ones like <img />.
Braces hold any expression — strings and numbers render as text; booleans, null and undefined render nothing. Statements like if stay above the return, feeding variables into the braces.
A component returns one root value; a fragment (<>...</>) groups siblings without adding anything to the DOM.
Vite’s error overlay reports compile errors in the browser — read it, fix it, and it goes away by itself.
Organize with one component per file, PascalCase names, .tsx for JSX files and .ts for the rest.
Default exports carry components; named exports carry data and helpers. Files import what they use, by relative path.
Components need no special TypeScript type — they’re plain functions, and inference follows your data across files.
A missing closing tag breaks everything after it — when an error seems impossible, look above the reported line.
Where to Go From Here?
The Learning Tracker now has real architecture: four components with clear jobs, each in a findable file. But you saw the crack in the foundation — CourseCard and LevelBadge read one shared course object, so they can only ever show one course. A catalog of identical cards isn’t much of a catalog.
Chaf yilxidivvm ziak aq e sad wi wareuho kawe xjof pzu iarnile, bwa qut cewdzuewv qoxuuwo ecyugiyrp. Boobq rudrg jfuw dvufk, ugg hgoc’tu lhe buwnolb ix jdo juvd dcipnam — ggiqu huaw funq mihowww heovjc we fervjam oyz waupru poo gegr uq.
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.