{"id":"l-rf-1-4","courseId":"react-fundamentals","lang":"en","kind":"lesson","summary":"Read and write JSX without fighting the compiler by knowing what it compiles to, how curly braces work, and which attribute and rendering rules differ from HTML.","html":"<p>JSX looks like HTML, so your first instinct is to write HTML. Most of the time that works. Then you write <code dir=\"ltr\">class=&quot;card&quot;</code> and get a console warning, or put an <code dir=\"ltr\">if</code> inside curly braces and get a compile error that points at the wrong line. Every one of these surprises makes sense once you see what JSX really is.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">JSX is function calls</h2>\n<p>Vite compiles each JSX tag into a function call. This component:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"jsx\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">JSX</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">function</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> MovieCard</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">() {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  return</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">h2</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> className</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"title\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>Arrival&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">h2</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>;</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<p>compiles to roughly this:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-index=\"1\"><div class=\"code-head\"><span class=\"code-title\">JavaScript</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">import</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> { jsx } </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">from</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 'react/jsx-runtime'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">function</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> MovieCard</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">() {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  return</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> jsx</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'h2'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, { className: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'title'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, children: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'Arrival'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> });</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<p>The call returns a plain JavaScript object, a <strong>React element</strong>: a lightweight description of what should be on screen, with a <code dir=\"ltr\">type</code> and some <code dir=\"ltr\">props</code>. You can model the idea with a tiny function of your own:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-run=\"js\" data-index=\"2\"><div class=\"code-head\"><span class=\"code-title\">JavaScript</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn code-run\" data-action=\"run\" data-lang=\"js\" data-index=\"2\" aria-label=\"Run this example\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z\"/></svg><span>Run</span></button><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">function</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> h</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#953800;--shiki-dark:#FFA657\">type</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">props</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">...</span><span style=\"color:#953800;--shiki-dark:#FFA657\">children</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  return</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> { type, props: { </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">...</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">props, children } };</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> card</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> h</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'article'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, { className: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'card'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> },</span></span>\n<span class=\"line\"><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">  h</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'h2'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">null</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'Arrival'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">),</span></span>\n<span class=\"line\"><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">  h</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'p'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">null</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">2016</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">),</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(card.type);                         </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// article</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(card.props.className);              </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// card</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(card.props.children[</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">0</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">].props.children); </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// [\"Arrival\"]</span></span></code></pre><div class=\"run-output\" data-run-output=\"2\" hidden></div></div>\n<p>React’s real elements carry a little more information, but the shape is the same. React walks this tree of objects and creates or updates the DOM to match.</p>\n<p>Notice that the compiled code imports <code dir=\"ltr\">jsx</code> for you. That’s why modern React files don’t start with <code dir=\"ltr\">import React from 'react'</code>. Older tutorials include that line because the previous compiler turned JSX into <code dir=\"ltr\">React.createElement(…)</code> calls, which needed <code dir=\"ltr\">React</code> in scope. Since React 17’s automatic JSX runtime it’s unnecessary, so leave it out.</p>\n<figure class=\"diagram\"><svg class=\"diagram-svg\" focusable=\"false\" viewBox=\"0 0 700 200\" role=\"img\" aria-labelledby=\"l-rf-1-4-t1\"><defs><marker id=\"arrow-l-rf-1-4-1\" viewBox=\"0 0 10 10\" refX=\"9\" refY=\"5\" markerWidth=\"7\" markerHeight=\"7\" orient=\"auto-start-reverse\"><path d=\"M0 0 L10 5 L0 10 z\" class=\"d-arrowhead\"/></marker></defs>\n  <title id=\"l-rf-1-4-t1\">JSX source is compiled into a jsx function call, which returns a plain object with type and props; React then renders that object into a real DOM node.</title>\n  <rect class=\"d-box\" x=\"10\" y=\"50\" width=\"190\" height=\"90\" rx=\"12\"/>\n  <text class=\"d-label-muted\" x=\"105\" y=\"40\" text-anchor=\"middle\">you write</text>\n  <text class=\"d-code\" x=\"105\" y=\"100\" text-anchor=\"middle\">&lt;h2&gt;Arrival&lt;/h2&gt;</text>\n  <rect class=\"d-box-primary\" x=\"250\" y=\"50\" width=\"200\" height=\"90\" rx=\"12\"/>\n  <text class=\"d-label-muted\" x=\"350\" y=\"40\" text-anchor=\"middle\">compiler makes</text>\n  <text class=\"d-code\" x=\"350\" y=\"88\" text-anchor=\"middle\">{ type: 'h2',</text>\n  <text class=\"d-code\" x=\"350\" y=\"110\" text-anchor=\"middle\">  props: {…} }</text>\n  <rect class=\"d-box-success\" x=\"500\" y=\"50\" width=\"190\" height=\"90\" rx=\"12\"/>\n  <text class=\"d-label-muted\" x=\"595\" y=\"40\" text-anchor=\"middle\">React commits</text>\n  <text class=\"d-label-strong\" x=\"595\" y=\"100\" text-anchor=\"middle\">DOM &lt;h2&gt; node</text>\n  <path class=\"d-arrow\" d=\"M200 95 L248 95\" marker-end=\"url(#arrow-l-rf-1-4-1)\"/>\n  <path class=\"d-arrow\" d=\"M450 95 L498 95\" marker-end=\"url(#arrow-l-rf-1-4-1)\"/>\n  <text class=\"d-label-muted\" x=\"350\" y=\"180\" text-anchor=\"middle\">React elements are descriptions, not DOM nodes</text>\n</svg><figcaption>JSX becomes an element object, then DOM</figcaption></figure>\n<p>Once you know JSX is function calls, the rules below stop feeling arbitrary.</p>\n<h2 id=\"sec-2\" tabindex=\"-1\">Curly braces hold expressions</h2>\n<p>Inside JSX, <code dir=\"ltr\">{ }</code> switches back to JavaScript. Whatever goes inside becomes an argument to a function call, so it must be an <strong>expression</strong>: something that produces a value.</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"jsx\" data-index=\"3\"><div class=\"code-head\"><span class=\"code-title\">JSX</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">function</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> MovieCard</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">() {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> movie</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> { title: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">'Arrival'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, year: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">2016</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, rating: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">8</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> };</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> age</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 2026</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> -</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> movie.year;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  return</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">article</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">      &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">h2</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">{</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">movie.title.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">toUpperCase</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">()</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">h2</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">      &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">p</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">{</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">movie.year</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> · </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">{</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">age</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> years old&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">p</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">      &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">p</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">{</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">movie.rating </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">>=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 8</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> ?</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 'Must watch'</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> :</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 'Maybe'</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">p</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    &#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">article</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  );</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<p>Variables, property access, method calls, arithmetic and the ternary operator <code dir=\"ltr\">a ? b : c</code> are all expressions. <code dir=\"ltr\">if</code>, <code dir=\"ltr\">for</code> and <code dir=\"ltr\">const</code> are statements, so they can’t go inside braces, in the same way you can’t pass <code dir=\"ltr\">if (x) {}</code> as a function argument. When the logic gets bigger, compute a variable above the <code dir=\"ltr\">return</code> and use that.</p>\n<p>Here’s the same card with a label that needs real branching. The <code dir=\"ltr\">if</code> lives in plain JavaScript above the <code dir=\"ltr\">return</code>, and JSX only receives the finished value:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"jsx\" data-index=\"4\"><div class=\"code-head\"><span class=\"code-title\">JSX</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">function</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\"> MovieCard</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">({ </span><span style=\"color:#953800;--shiki-dark:#FFA657\">movie</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> }) {</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  let</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> label;</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  if</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (movie.rating </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">>=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 9</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) {</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    label </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 'Masterpiece'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  } </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">else</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> if</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (movie.rating </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">>=</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 7</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) {</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    label </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 'Must watch'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  } </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">else</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">    label </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\"> 'Maybe'</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">;</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  }</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  return</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">p</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">{</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">movie.title</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">: </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">{</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">label</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">p</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">>;</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}</span></span></code></pre></div>\n<p>This pattern scales. Your JSX stays a readable picture of the output, and the decisions sit in ordinary code you can read, test and debug like any other function. (The <code dir=\"ltr\">{ movie }</code> in the parameter list is a prop; you’ll learn props properly in the next section.)</p>\n<p>Attributes take braces too: <code dir=\"ltr\">&lt;img src={movie.poster} alt={movie.title} /&gt;</code>. Don’t wrap braces in quotes; <code dir=\"ltr\">src=&quot;{poster}&quot;</code> is the literal string <code dir=\"ltr\">{poster}</code>.</p>\n<h2 id=\"sec-3\" tabindex=\"-1\">Attributes are JavaScript names</h2>\n<p>Because props become keys of a JavaScript object, they follow JavaScript naming:</p>\n<ul>\n<li><code dir=\"ltr\">class</code> → <code dir=\"ltr\">className</code>, <code dir=\"ltr\">for</code> → <code dir=\"ltr\">htmlFor</code> (both are reserved words in JS).</li>\n<li>Multi-word attributes are camelCase: <code dir=\"ltr\">onClick</code>, <code dir=\"ltr\">tabIndex</code>, <code dir=\"ltr\">autoComplete</code>, <code dir=\"ltr\">maxLength</code>.</li>\n<li><code dir=\"ltr\">aria-*</code> and <code dir=\"ltr\">data-*</code> attributes keep their dashes: <code dir=\"ltr\">aria-label=&quot;Remove&quot;</code>, <code dir=\"ltr\">data-id=&quot;42&quot;</code>.</li>\n<li><code dir=\"ltr\">style</code> takes an object, with camelCase CSS properties: <code dir=\"ltr\">style={{ marginTop: 8 }}</code>. A bare number means pixels for most properties. The double braces are an object literal inside a JSX expression.</li>\n</ul>\n<h2 id=\"sec-4\" tabindex=\"-1\">Close every tag</h2>\n<p>JSX is stricter than HTML. Every element closes: <code dir=\"ltr\">&lt;img /&gt;</code>, <code dir=\"ltr\">&lt;input /&gt;</code>, <code dir=\"ltr\">&lt;br /&gt;</code>. A component returns one root, wrapped in a fragment <code dir=\"ltr\">&lt;&gt;…&lt;/&gt;</code> if you need siblings. Comments go inside braces: <code dir=\"ltr\">{/* like this */}</code>.</p>\n<h2 id=\"sec-5\" tabindex=\"-1\">What can be rendered</h2>\n<div class=\"table-wrap\" role=\"region\" aria-label=\"Scrollable table\" tabindex=\"0\"><table><thead>\n<tr>\n<th>You put in braces</th>\n<th>What appears</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>A string or number</td>\n<td>The text (<code dir=\"ltr\">0</code> included)</td>\n</tr>\n<tr>\n<td>A React element</td>\n<td>That element</td>\n</tr>\n<tr>\n<td>An array of elements</td>\n<td>Each element in order</td>\n</tr>\n<tr>\n<td><code dir=\"ltr\">null</code>, <code dir=\"ltr\">undefined</code>, <code dir=\"ltr\">true</code>, <code dir=\"ltr\">false</code></td>\n<td>Nothing</td>\n</tr>\n<tr>\n<td>A plain object</td>\n<td>Error: “Objects are not valid as a React child”</td>\n</tr>\n</tbody>\n</table></div><p>The “nothing” row is what makes conditional rendering work, and the <code dir=\"ltr\">0</code> in the first row is a classic trap you’ll meet in the Conditional Rendering lesson.</p>\n<aside class=\"callout callout-mistake\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\"/><path d=\"M12 9v4\"/><path d=\"M12 17h.01\"/></svg><span><span class=\"callout-kind\">Common mistake</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>Rendering a whole object</span></p>\n<p><code dir=\"ltr\">&lt;p&gt;{movie}&lt;/p&gt;</code> crashes the app with “Objects are not valid as a React child”. You almost always meant one field: <code dir=\"ltr\">{movie.title}</code>. If you want to inspect data while debugging, render <code dir=\"ltr\">{JSON.stringify(movie)}</code> temporarily.</p>\n</aside>\n<aside class=\"callout callout-why\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><circle cx=\"12\" cy=\"12\" r=\"10\"/><circle cx=\"12\" cy=\"12\" r=\"6\"/><circle cx=\"12\" cy=\"12\" r=\"2\"/></svg><span><span class=\"callout-kind\">Why this matters</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>Text is escaped for you</span></p>\n<p>React escapes every string you render. If a movie title from an API contains <code dir=\"ltr\">&lt;script&gt;</code>, it shows up as visible characters instead of running. You get protection from HTML injection for free, as long as you don’t bypass it with <code dir=\"ltr\">dangerouslySetInnerHTML</code>.</p>\n</aside>\n<p>You can now read any JSX and predict what it does. In the next section you’ll stop hard-coding “Arrival” and learn to think in components, splitting a whole screen into pieces that receive their data through props.</p>\n","toc":[{"id":"sec-1","text":"JSX is function calls","level":2},{"id":"sec-2","text":"Curly braces hold expressions","level":2},{"id":"sec-3","text":"Attributes are JavaScript names","level":2},{"id":"sec-4","text":"Close every tag","level":2},{"id":"sec-5","text":"What can be rendered","level":2}],"takeaways":["JSX compiles to function calls that create plain JavaScript objects describing the UI; React turns those objects into DOM.","Curly braces embed any JavaScript expression (a value), but never a statement such as <code dir=\"ltr\">if</code> or <code dir=\"ltr\">for</code>.","Attributes are camelCase JavaScript names, so you write <code dir=\"ltr\">className</code>, <code dir=\"ltr\">htmlFor</code>, <code dir=\"ltr\">onClick</code> and pass <code dir=\"ltr\">style</code> an object.","Strings and numbers render as text, while <code dir=\"ltr\">null</code>, <code dir=\"ltr\">undefined</code>, <code dir=\"ltr\">true</code> and <code dir=\"ltr\">false</code> render nothing and plain objects throw."],"further":[{"title":"Writing Markup with JSX","url":"https://react.dev/learn/writing-markup-with-jsx"},{"title":"JavaScript in JSX with Curly Braces","url":"https://react.dev/learn/javascript-in-jsx-with-curly-braces"},{"title":"Common components (props on every DOM element)","url":"https://react.dev/reference/react-dom/components/common"}],"quiz":[{"q":"<p>What does <code dir=\"ltr\">&lt;h2 className=&quot;title&quot;&gt;{movie.title}&lt;/h2&gt;</code> become after Vite compiles it?</p>","options":[{"text":"A function call that creates an object with the type <code dir=\"ltr\">'h2'</code> and props <code dir=\"ltr\">{ className: 'title', children: movie.title }</code>.","why":"Correct. JSX is syntax for creating element objects. React reads those objects to decide what DOM to create or update."},{"text":"A string of HTML that React inserts with innerHTML.","why":"React never builds HTML strings for rendering. That’s also why JSX text is safe from HTML injection by default."},{"text":"A real <code dir=\"ltr\">HTMLHeadingElement</code> created with <code dir=\"ltr\">document.createElement</code>.","why":"JSX only produces lightweight description objects. React creates or reuses DOM nodes later, during commit."},{"text":"It stays as JSX and the browser parses it natively.","why":"Browsers don’t understand JSX. Without a compiler step the page throws a syntax error."}],"answer":0},{"q":"<p>Which line is valid JSX?</p>","options":[{"text":"<code dir=\"ltr\">&lt;p&gt;{if (watched) 'Seen'}&lt;/p&gt;</code>","why":"<code dir=\"ltr\">if</code> is a statement, not an expression, so it can’t go inside curly braces. Use a ternary or compute the value before <code dir=\"ltr\">return</code>."},{"text":"<code dir=\"ltr\">&lt;img src=&quot;{poster}&quot;&gt;</code>","why":"Quotes make it the literal string “{poster}”, and the tag isn’t closed. Write <code dir=\"ltr\">src={poster}</code> and self-close with <code dir=\"ltr\">/&gt;</code>."},{"text":"<code dir=\"ltr\">&lt;label class=&quot;field&quot; for=&quot;title&quot;&gt;Title&lt;/label&gt;</code>","why":"In JSX these are <code dir=\"ltr\">className</code> and <code dir=\"ltr\">htmlFor</code>, because <code dir=\"ltr\">class</code> and <code dir=\"ltr\">for</code> are reserved words in JavaScript."},{"text":"<code dir=\"ltr\">&lt;div style={{ marginTop: 8, fontWeight: 'bold' }}&gt;Hi&lt;/div&gt;</code>","why":"Correct. The outer braces enter JavaScript, the inner braces are an object, and CSS properties are camelCase. A bare number means pixels."}],"answer":3},{"q":"<p><code dir=\"ltr\">movie</code> is <code dir=\"ltr\">{ title: 'Arrival', year: 2016 }</code>. What happens with <code dir=\"ltr\">&lt;p&gt;{movie}&lt;/p&gt;</code>?</p>","options":[{"text":"It renders “[object Object]”.","why":"React doesn’t call toString on objects. It refuses to render them at all."},{"text":"React throws “Objects are not valid as a React child”.","why":"Correct. React can render strings, numbers, elements and arrays of those. Render a field instead, like <code dir=\"ltr\">{movie.title}</code>."},{"text":"It renders the object as JSON.","why":"React never serializes objects for you. Use <code dir=\"ltr\">JSON.stringify(movie)</code> if you really want to show JSON."},{"text":"It renders nothing, like <code dir=\"ltr\">null</code>.","why":"Only <code dir=\"ltr\">null</code>, <code dir=\"ltr\">undefined</code> and booleans render nothing. A plain object is an error."}],"answer":1},{"q":"<p>A movie’s description from your API contains <code dir=\"ltr\">&lt;b&gt;Must watch&lt;/b&gt;</code>. You render <code dir=\"ltr\">&lt;p&gt;{movie.description}&lt;/p&gt;</code>. What appears?</p>","options":[{"text":"“Must watch” in bold.","why":"React doesn’t parse strings as HTML, which protects you from injected markup."},{"text":"An error, because the string contains angle brackets.","why":"Any string is valid content. React escapes it and shows it as text."},{"text":"The literal text <code dir=\"ltr\">&lt;b&gt;Must watch&lt;/b&gt;</code>, tags and all.","why":"Correct. React escapes text content, so markup in data shows up as characters instead of running. That’s a key defence against cross-site scripting."}],"answer":2}],"exercise":{"kind":"web","mode":"js","starter":{"js":"// A tiny stand-in for what JSX compiles to.\nfunction h(type, props, ...children) {\n  return { type, props: { ...props, children } };\n}\n\n// Write the h() calls this JSX compiles to:\n//\n// <article className=\"card\">\n//   <h2>{movie.title}</h2>\n//   <p>{movie.year}</p>\n// </article>\nfunction buildCard(movie) {\n  return h('article', null);\n}\n"},"solution":{"js":"// A tiny stand-in for what JSX compiles to.\nfunction h(type, props, ...children) {\n  return { type, props: { ...props, children } };\n}\n\n// Write the h() calls this JSX compiles to:\n//\n// <article className=\"card\">\n//   <h2>{movie.title}</h2>\n//   <p>{movie.year}</p>\n// </article>\nfunction buildCard(movie) {\n  return h('article', { className: 'card' },\n    h('h2', null, movie.title),\n    h('p', null, movie.year),\n  );\n}\n"},"checks":[{"id":"root","test":"const el = buildCard({ title: 'Arrival', year: 2016 }); return el.type === 'article' && el.props.className === 'card';"},{"id":"two-children","test":"const el = buildCard({ title: 'Arrival', year: 2016 }); const c = el.props.children; return Array.isArray(c) && c.length === 2 && c[0].type === 'h2' && c[1].type === 'p';"},{"id":"title-text","test":"const el = buildCard({ title: 'Arrival', year: 2016 }); return [].concat(el.props.children[0].props.children).join('') === 'Arrival';"},{"id":"uses-data","test":"const el = buildCard({ title: 'Past Lives', year: 2023 }); const [t, y] = el.props.children; return [].concat(t.props.children).join('') === 'Past Lives' && [].concat(y.props.children).join('') === '2023';"}],"title":"Compile JSX by hand","prompt":"<p>JSX is function calls. The starter gives you <code dir=\"ltr\">h(type, props, ...children)</code>, a simplified version of what the compiler produces. Complete <code dir=\"ltr\">buildCard(movie)</code> so it returns exactly what this JSX would create:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"jsx\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">JSX</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">article</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> className</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"card\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">h2</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">{</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">movie.title</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">h2</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  &#x3C;</span><span style=\"color:#116329;--shiki-dark:#7EE787\">p</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">{</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">movie.year</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">}</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">p</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">&#x3C;/</span><span style=\"color:#116329;--shiki-dark:#7EE787\">article</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">></span></span></code></pre></div>\n<p>Use the <code dir=\"ltr\">movie</code> argument, not hard-coded values.</p>\n","hints":["The first argument is the tag name as a string, the second is the props object (or <code dir=\"ltr\">null</code>), and every argument after that is a child.","Attributes go in the props object: <code dir=\"ltr\">{ className: 'card' }</code>.","Children can be other <code dir=\"ltr\">h()</code> calls: <code dir=\"ltr\">h('article', { className: 'card' }, h('h2', null, movie.title), …)</code>."],"explanation":"<p>Each JSX tag becomes one call: the tag name, then an object of attributes, then the children in order. Curly-brace expressions like <code dir=\"ltr\">{movie.title}</code> are just arguments, which is why only expressions are allowed inside them. React’s real <code dir=\"ltr\">jsx()</code> differs in details (it receives children already inside the props object instead of as extra arguments, a single child isn’t wrapped in an array, and keys are passed separately), but the tree of plain objects is the same idea.</p>\n","checkLabels":{"root":"The root is an “article” with className “card”","two-children":"It has two children: an “h2” then a “p”","title-text":"The h2 contains the movie title","uses-data":"It works for any movie, not only Arrival"}},"words":713,"readMinutes":3}