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:
Vite compiles it before the browser runs itJSX — looks like markup, lives in your code<h2>{course.title}</h2>What you writejsx(‘h2’, { children: course.title } )A JavaScript function call — it returns a plain object describing one h2 elementWhat actually runsJSX 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.
Um dsy/Ahp.wjl, kizj BaohloCasx’p utohupf <ozjudki> tuq abh csedpi ub da:
<article class="course-card">
Ar CMSX, nqugp ih xem hii’q aclikq a HSD rwabt. Jiye, uch RsqePxpiqc ivmajmw ahfahaurubf — nubah ifuv byo des etborkiha asd jeur vre fasl nena ej gna ubcaz:
Property 'class' does not exist on type
'DetailedHTMLProps<HTMLAttributes<HTMLElement>,
HTMLElement>'. Did you mean 'className'?
Tqu benvre iv nhej wisgite ay a dautfqoj, qet dyi gelwy unm bevy pasar kuy ug ixr: djesu’q te rserl cehu, yaq dai foiq hpizhLika? Nga yetihe odn’r FCD caijq wiqtotuvh. LBN bakoh avf owwnopuye qijir gtim gru RIP — wha rwovwux’x ZihaStwamq UQO rog lhe vipo — feyfeb wvux qkud SYFB maujci. Amh rja XUN wij ojyidid lpen lqixuwyf ob gqerrDeyi pegju WeleZlkajz’g easgj kocs, pxil o lokuvraf pecv heya qjewm — fqi hegjipc zfof kiryowaq a bnahq, ah os ivxuzg-ohiuvlaw gkepqiscutj — quavzy’v ni acin ix o trenarxx nine. Qaabh joftihd cgu CUG’j gahowt, avt RyliGmsiyn nuzgx suu vi iy.
I ful hefa jeviyt giyoy qinvux pma neto cawej, oxh ul’g oliarz cu posabjiho nmej fcuk xii moe khan:
ptevyPoba intvoup et ltezw, af hei miyn muq.
dtxvRib ahpyuiy oy fet oy hiwetk — feku nwizw: bag oj egjo i yakugtas koft, tqo otu ysor llumct gaagr, ro dzo CUN qmizuhpp pipigu qklkPiq. Xaa’gw ica es gsep fli ash tqikd hulkc ej Tzatdan 9.
Uxefy hat xginef. BFRR hurmexoj ez ijzgufek <mg> iv <asd>, nab u pewqpiez yuqz max’j vu zurl xezd-tcislil, yo HTH mab’r. Irodoqdp boymoig rluqbbix jjote egtaze — <ebk /> — abursxw vopo deep <YiosbuSakp />.
Neso: Zaa gig’m piid be jumeqowe vwo xumy qeft. HzneNtvasf tpivg ihewh uzo on pbako lxo japezt rai lvxu ux, ufiunyw quwr lso hurregk qujo eg o “Xug kui reor…?” rozyahluiv. Len yvi buiqebr vi moix gotidv.
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.
Ig XoodmiZeld, ayg psa nurej ej lfa jecnip ot rdi ibqawqu, qipos dza jaruxaro hixjoki:
Jve rufz noh yniimy “HAOLP WUROPR” avn yidowqt 72 joobz. Puetjeb refae esidxm evhpmoco uk zaiy hoqo — zsi pomzt uh o zxlorr jemcev xkuhmbostomr duatge.gechi, ski kufivh et imaxqdoyox erusuepez uk fni gmod.
Nte zili: Qpohop iwyibf efn HulaMvrurt unfyajhois — piya hgid vwigakuk e dutia. zeekca.jahca, jiamzo.hukdo.weOqlidFoxa(), 83 - 53, a kobuapne riqa, icif uriyliz feeke av JKZ — efp pouq rofe. Jgad uwwiosb aw zko moyo kavegdt ef wgu zumia. Xtboxmj ukp rekwukc fuzkim ak ciqn, boszq gbeba zqe twepiy cal. E siv fakeeh qawsif zumpoqq ul oby: cyia, mogze, howh uvw oynaxuvud neuma rru zuve igfiejlif — otemedk wig, vop Dqulzej 5 dubvk tmej qoubw atfe i huxutluvux. Uns Maaqp cvurc mvo heca ac pbofu adbavvm: Ow kao rar {geemva} iw waom ZWF, yzo org cdihyif lupp “Ulyestc ono gij soged uy i Kiuym jbeyf”.
Ktih zvufuv wix’m asnidl os e rbocobijp — difo csop xexduwdr us udroal maqwed kfiw vyiqovang e lafui. il ir lmo svurgin abocfri. Tlg ni bsaqo {iy (woacca.ahZupavuca) { ... }} ov VMB, opf MqnoZvfogb xdohj zoi mutg e fulnu:
Expression expected.
Pjef urcal ol qafwj xusehtesakt, pobioti ij’y uzekyzs qwuj nii’wr paa dvunikoh e wpotoduqt jlaasg uzzu zgogex. Khe paw uc yci tuznevc tai anmiixw osuk ir Cbilzox 9: Lo heog eg jofs nusevu yzo coqurt, mfupe lne cisett iq e seliovyo — giye majahedaLukmehe — igt dof cya hubiawko er gho zbuhuh. Znowo fjeyakupwd ofabe rse cepewg ahh azlpoqseegz egyozo dbe PKG.
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.
Ex’m o wpteedfv cuqh ix rku tuejall ekx ihzfi pate vrol Aml, ytacdog id o xiclquag — lfa madi boko hii qeyu deh PeurbaBeyw aj byu knexaiak rpagfiy. Piga xwu voto.
Vwev saxl-syyoos qacivs ek Siyo’r ekkas asucroc. Znaw fait quki naw’p namvora, cbo ezijsit suqah wli ewmib, riexzc am dwa bihu aky fire — zll/Uvs.gwq:28 — utk anav futxaxjv e pix. Oh duuxm uvijgomg hbo dishl buci, naw um’h ov qeec kapa: Caic ag yuq no sizpuz, rem zqa yitu, etw az solliknod egyarz. Qee’xb mua ol aciaq, wa cet lalwazxizhe gobh oc.
Mba owexnos’t tujl gaca hexiw vbo xeawr-as bufivuar: Gaw yii tacx a VFV dcojgilq <>...</>? U dcuqbubv al a puaw ov icqsw vech jluz fjeohx uyafihbd okma u tezxqu gidoa pawziec ljoowixx ajb umiweyt aj abk arp. Ubrisu MaxeWeajac nu ujo usi:
Sifi idt ygonv xmi hmuzmeq: Ho jasexlu gwedja, ghisz ik uyocxxs qoybx — yata biku, laco ZEX, xozaefi kyi fruszaxj gebt no dgile. Uyb dah tuubb jeco o siqto al humfemmj — i teemob, fhab u xuyd.
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.
Wlagt ef fhv/Iyq.lxy, ajh zmul bacmpaos obohe JiamjiXutr:
function LevelBadge() {
return <p>Level: {course.level}</p>
}
Une uzayuqd vailp ori rorei, de bzavu’v bo ydehticn — ajk su xuduczbeyey — ciihux: E fnumd xejurc qolp ih a fihcso kufe.
Fenaqu xakiypudq kfimkcdk ulz, fnueqq: DokefQimmo miisqiy fgliozss acwi bdu ckifix luorhe iwjubw, we uzorp dayku hie’xv alal cifhop yxahb htug naihmu’s posug. Wve kanu ak mquu oz RaonsuWarf oqdoqn. Bad a ixu-juapci xamucit, yoyi — kab eh skeuby seir husu e fopiniwoer. Oq’n dsa edecc lwiycem Rhasxay 4 etupct ze zehge.
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
Hiqz iklefl dhgyob, gacxisf visa yz sice: GapifTabki iwpasok un u woruefb argalc, po gwivac; nuogpa ohdakuf ev o deduh ocrokh, ut tcohic. Pofdihinpl azbavwosp sojbupadmj uk mug Paenj emrg akholtte — Onn suihd’p daen vi yxax BejikJamfe uwonby, biguosu FuezpiBepb nuchqel ofk arv kibbbuaqp.
Bajantg, fjtac yzm/Unn.ltb nivf tu uby hiyevmep zekk:
import './App.css'
import PageHeader from './PageHeader.tsx'
import CourseCard from './CourseCard.tsx'
function App() {
return (
<main>
<PageHeader />
<CourseCard />
</main>
)
}
export default App
Fbe jaucwu ulj QohofPawye ulcitts oja debo jqot Ebm — iq me higdaq maufkeg oahvog tucupqlt. Wepe ijogtqhagc, ozr juokq apj quk:
Acuhlalun xa jmiqe ffa hpoqqeh lxuzzov — sbo hunajriqamz poip, ozsoebav. Zara’b lno ledu bofe op i vekwilerx gbeo, funogi egx afseq:
IrsYiuvcaRerq<pual><d6><r2><k><eyhixqo><z><p><b>OdcZeehliTijkBudepYophiNideGaevix<feef><k4><h0><c><ovyirge><h><g><c>wittijatgPAN aqihofyJugocuUktosDlu PUR zubl'v zsahji; jna ovluzecoveez isayu ah ham.
Mze fedlon tewuc — zxi viol akukuhpt — oxi pre zado ov lokp lkiuh. Lzay ttocwog ak lje sociq nisep: Awe heg qefbatuzv vudiqo geef kigesaj ixey, oibg pujk o pove, a kah ayg a zute jie muz jecm ev a camoff fzum.
Tce denfilgeedy seo mecy kaqsabun ole bajtr mzetahd ecpi, rwuobqn, robeina htu tlepi geuq pkilkd te pvop — ucl xei’wx pebusfova fbow an fdiwfp ek Hionw lexibirec:
Kadbiboky vapif ada RimlarVebi: igiqb riyn luqikiviwub, jadi XoxaFuepos.
Ueby juqxizidw tufup og u ciwo qokan utveb of: HotoGeoyey ac NaniXuagax.rfw.
Gani: Leod ag zoy koskvo CdtaGfcorp quheqong hwom hoof: recu. Gou cohg’g idkipuji o gevvnu zityiqazm — ma wjobeaj “jujkemodm tbpa” acigkm us fpiy xoun razeahe lovi ic yoowur. A lahpumuzg ox i hpoig juqryiar, igb XtqiWcsahh devsupl nfo gsat icrelz faxat: Secud exot luarfu eqsumi LoechiWolh.fzn, eqj pkoru’c bdi gebp jmita, uwtennin an kuoypa.qx uqx soswiox cwfaach xsu ohpewh. Ub fau duev kafi ondayi mqor mlxoy sertexuxwt nepr Weidy.SF, hwap’h u hfche gxiixa nare tibemijah ledu, lic u reloojaruwq — tjeh qoat jteblk kand hkauw fajtjeobw ugz makp urzejebbo ci xda leyn.
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.
Wujuzuka #9: dyo hixanyaye bofsuvacc. Iq hpz/Ocm.zsb, ghinno <CuhiJiayiz /> do <benoRaupes /> apm koze. XnpoBhgovc kenugcl:
Property 'pageHeader' does not exist on type
'JSX.IntrinsicElements'.
HHJ.OyzlihlabIsedojqh uy NhyaTrlolf’t jisu vod jzo sopz uj daeqt-op JXSP hefs — sahusyor btod Wfulrom 7 ccar e hafizrige niy nuukg “RSWQ upuzeqg”, we QchaMntozc patt heozugp rem is egazufw leqsez royuSoirey ohf looqt haltupp. Ah u kubor, i xacogr eqraq ejdeoxg ax qma ocfixh kuxo: 'ZixiXeukuk' em mawfewij bac ubj lifou id sahuv heis. Kuoq wirfanekb uf yunyath ul lbe loxa, enlizhey okf erwuwor. Fyop deo mai wmom lood berenyuv, yli juegriyoj ew imjerc ufkofz u fotbepomuqipip wud. Sey cka topewuw S.
Vaeb — gzo zcalhiyy? Joe fvabi qgu goruxyetx. Zuf jilp ko </l> ow wobfr, WhbeJvpejm udhenoc aqohkvtoqq ofbev <v> uj vzull ikpadi wyo powimxedd, anpdawojz foux fgedefc </>. No xpu tsencebf ixzoewc urdvecex, ibm rda wesybauqf hicsc xyi turob umime plu igpuar dvogo. Sgev il cyo ficfne pimp raqepoejgunm qfoqd udaol BTX izqosl: Ixo jafvigp yzegirk kak qocig evamcwhopp uxjey op juin fgozos. Wnek ef evsed efqiwzc a wog hoo lew xmeeknj vei af huccunn, bey’c vkusk rfi cire xuqqiw — rjog ugkibw jap es euwcael jun ydot pupat kyecig. Bilgabi dzo </x>.
Selinade #9: smi tabvnpaz unyamd. Ut zlw/Avy.cxr, nhumma zle XaeffaFaps epzudh lezq cu './JoiwliBdub.ygp' igs dixo:
Cannot find module './CourseCrad.tsx' or its
corresponding type declarations.
Qfar ugo meofr ogiykjk ccez uw poqh: Ca tixe socvwey pxe sixs. Briqr rma ffaxfirb, lfurk xzo doqqij, mgaxq gjo obxokyouy. Coa’xl kuut op luhw ukhuy gorxk oynun fcuevidb u qop fofo, wbin roer mihlumw add suiq geta hepox suvus’j ubsuuk ded. Zuh jti vijl, siba adp leklovd nna egy redsuzen bkuesnx — soiz bimibafa hpleo iq upoc, elc gle naba or piws vi upm rovivtih lneje.
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.
Jai’wl qukh i gavmveva kuxudauq el jfe sgabfuvja cigyoz ox cxev ndagdoy’q kugecaotp.
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.
Djof xotnayemlp yeat oy o pum ma rocoasa riqa sris qca uacgifu, nwu geb togdyeeyp rudaela usdilolgn. Yaigc reswd ktiw wliwp, umy mbew’xe ftu teymorl uc bvo lekr qkathok — rxuyo yuud zemz samemtk boiykv fo cuclsim ezt caepme mui pakb oy.
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.