cf.completefrontend
Open lab My progress
THE REACT FIELD GUIDE

Writing markup with JSX

See what JSX compiles to, jsx() calls from react/jsx-runtime, and why that gives JSX its rules: one root, closed tags, className, htmlFor, and camelCase.

By the end, you can
  • 01
    Read what JSX compiles toTurn a tag into its jsx() call from react/jsx-runtime, say what the element object it returns holds, and compare it with the older React.createElement.
  • 02
    Explain JSX's rulesSay why a component returns one root (or a Fragment), why every tag closes, and why attributes are spelled className, htmlFor, and in camelCase.
  • 03
    Turn HTML into JSXConvert real HTML into a working component, and read the compiler's errors when something is left the HTML way.

HTML that doesn't paste

In Your first component, every component returned markup that looked like HTML. That markup is JSX. It looks so much like HTML that the first thing people try is pasting HTML into a component. Here is a sign-up box for a tea shop, as plain HTML. Its result is live:

A sign-up box in HTMLPop out in the code editor (opens in a new tab)HTML
<h2>Join the tea club</h2><p class="hint">One email a month.<br>New teas first.</p><label for="email">Email</label><input id="email" type="email"><button>Join</button>
ResultLoading…

Five lines: a heading, a hint with a line break, a label, an email box, and a button. Now the same five lines, pasted into a component as they are:

The same HTML pasted into a component (an error on purpose)Pop out in the code editor (opens in a new tab)JSX
export default function JoinBox() {  return (    <h2>Join the tea club</h2>    <p class="hint">One email a month.<br>New teas first.</p>    <label for="email">Email</label>    <input id="email" type="email">    <button>Join</button>  );}
ResultLoading…

Nothing shows. The result is a syntax error on line 4: Unexpected token, expected ",". The message is puzzling, because the compiler is confused: line 3 is already a whole tag, and in JavaScript nothing can follow it inside the brackets of a return. That's one of four things to fix. Here is the box as JSX:

The sign-up box as JSXPop out in the code editor (opens in a new tab)JSX
export default function JoinBox() {  return (    <section className="join-box">      <h2>Join the tea club</h2>      <p className="hint">One email a month.<br />New teas first.</p>      <label htmlFor="email">Email</label>      <input id="email" type="email" />      <button>Join</button>    </section>  );}
ResultLoading…
  1. One root. A <section> on line 3 holds everything, and closes on line 9.
  2. Closed tags. <br> became <br /> on line 5, and the <input> on line 7 ends with />.
  3. className in place of class, on lines 3 and 5.
  4. htmlFor in place of for, on line 6. Click the word Email in the result: the cursor jumps into the box, because htmlFor links the label to the input with the id email.

These rules aren't a list to memorize. They all come from one fact: before your code runs, every JSX tag becomes a JavaScript function call.

Three cards with arrows between them. The JSX tag h3 Green tea becomes the call jsx("h3", …), which returns a plain object with type "h3".<h3>Green tea</h3>JSX you writejsx("h3", {…})what a compiler writes{ type: "h3" }what jsx() returnsan element: a plain object
You write a tag. A compiler turns it into a jsx() call before the code runs, and the call returns an element: a plain object that describes the tag. React reads elements like this one to build the page.
Real-life analogyAn order slip at a café

At a busy café you say your order however you like, and the cashier writes it on a slip with fixed boxes. The kitchen never hears you. It works only from slips, so each order must fit the slip's boxes: one drink per slip, every box filled in the slip's own words.

In real life: You say "a green tea, large, no sugar"
In React: You write <h3 className="title">Green tea</h3>
In real life: The cashier writes it on a slip with fixed boxes: drink, size, notes
In React: The compiler writes jsx("h3", { className: "title", children: "Green tea" })
In real life: The slip itself, a piece of paper
In React: The element: a plain object with type and props
In real life: The kitchen makes the drink from the slip
In React: React makes the real <h3> from the element
The main idea

JSX is a way to write markup inside JavaScript. Before the code runs, a compiler turns every tag into a call to a function, jsx(), that returns a plain object describing the tag. Because every tag is a JavaScript call, JSX follows JavaScript's rules, not HTML's.

Every tag becomes a function call

Browsers don't understand JSX. A compiler, a tool that rewrites code, turns it into plain JavaScript first: Babel, TypeScript, esbuild, or Sucrase, usually run for you by your build tool. Every live example on this site is compiled by Sucrase, in your browser, a moment before it runs. Here is a small card:

export default function TeaCard() {  return (    <article className="tea-card">      <h3>Green tea</h3>      <p>$3</p>    </article>  );}
ResultLoading…

And here is what a compiler really turns it into. This is the output of TypeScript's compiler with the setting jsx: "react-jsx", React's modern JSX transform, formatted with Prettier. Babel and Sucrase write the same calls; the lesson's tests compile the card with all three and compare.

The tea card, compiled (the modern JSX transform)JavaScript
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
export default function TeaCard() {
  return _jsxs("article", {
    className: "tea-card",
    children: [
      _jsx("h3", { children: "Green tea" }),
      _jsx("p", { children: "$3" }),
    ],
  });
}

Read it line by line:

  1. Line 1 imports two functions, jsx and jsxs, from react/jsx-runtime, a part of the react package made for compilers. The compiler added this line; you never write it.
  2. Line 3: the <article> tag became a call. Its first argument is the type, the tag's name as a string, "article".
  3. Lines 3 to 9: its second argument is one object, the props. The attribute became a key on line 4, className: "tea-card", and the tags inside became children (lines 5 to 8).
  4. Lines 6 and 7: each child is its own call, with its text as children. A tag with one child uses jsx; the article, with two children in an array, uses jsxs. They build the same thing.

A capitalized tag works the same way, with one difference you met in the last lesson: <TeaCard /> becomes jsx(TeaCard, {}), with your function itself as the type, not a string.

So what does a jsx() call give back? An element: a plain JavaScript object that describes the tag, as you saw in How React works. Now you know where it comes from. This example stores one in a constant and prints two of its fields. Press Edit & run if the console isn't showing:

An element is a plain objectPop out in the code editor (opens in a new tab)JSX
const title = <h3 className="title">Green tea</h3>; console.log(title.type);console.log(title.props); export default function App() {  return title;}
ResultLoading…

Line 1 makes the element; nothing is on the page yet. Line 3 prints its type, h3, and line 4 its props: an object with className: "title" and children: "Green tea". Line 7 returns that same object, and React turns it into the real <h3> you see. An element is only a description; React reads it to decide what to put on the page. (It has a few more fields, like key and a hidden tag that marks it as a React element. React elements and the JSX runtime, in the course's last stage, looks at every one.)

Read it in React's own code

The functions the compiler imports are in packages/react/src/jsx/ReactJSX.js, at release v19.3.1, the version these examples run. It exports jsx, jsxs, and Fragment. In production both jsx and jsxs are jsxProd, at line 291 of ReactJSXElement.js, which takes the props object as it is and hands it to ReactElement, at line 170, the function that builds the object with type, key, and props.

Older React code compiled JSX differently. The classic transform turned every tag into React.createElement(type, props, ...children). Here is the same card compiled the classic way (TypeScript with jsx: "react"), from a file that started with import React from "react":

The tea card, compiled the classic wayJavaScript
import React from "react";
export default function TeaCard() {
  return React.createElement(
    "article",
    { className: "tea-card" },
    React.createElement("h3", null, "Green tea"),
    React.createElement("p", null, "$3"),
  );
}

The calls on lines 3, 6, and 7 name React, so every file with JSX had to import React (line 1), even when it used nothing else from it. The children are extra arguments (lines 6 and 7), and a tag without attributes gets null. React 17, in 2020, introduced the modern transform, which imports jsx itself. The React 19 upgrade guide made it required: React 19 uses it for features like passing ref as a prop, and in development it warns when an app still uses the classic one.

How to read this table: each row asks the same question of both transforms.

The classic and the modern JSX transform
QuestionClassic: React.createElementModern: jsx()
What does a tag turn into?A call to React.createElement(type, props, ...children).A call to jsx(type, props) or jsxs(type, props), with the children inside props.
Who imports the function?You: every file with JSX needs import React from "react".The compiler: it adds import { jsx } from "react/jsx-runtime" itself.
Where do the children go?Extra arguments after the props.props.children: one child, or an array for several.
Does React 19 accept it?It warns that your app uses an outdated JSX transform.Yes. React 19 needs it, for features like ref as a prop.
Can you call it yourself?Yes. createElement is still part of React, for building elements without JSX.You could, but react/jsx-runtime is made for compilers. By hand, use createElement.

createElement itself isn't gone. It's still the way to build elements by hand, without JSX, and it builds the same elements. This card has no JSX at all, and looks exactly like the one above:

The same card with createElement, no JSXPop out in the code editor (opens in a new tab)JSX
import { createElement } from "react"; export default function TeaCard() {  return createElement(    "article",    { className: "tea-card" },    createElement("h3", null, "Green tea"),    createElement("p", null, "$3"),  );}
ResultLoading…

Line 1 imports createElement by name. Lines 4 to 9 are three calls: the article, with its props on line 6, and its two children on lines 7 and 8. JSX is a shorter way to write calls like these; you can always do without it, and nobody does, because the markup is much easier to read.

Now compile your own. Pick an example or type any JSX in the box, and switch between the two transforms. The two last examples show the compiler stopping: the next two sections explain why.

Playground: what your JSX compiles to
What the compiler writescompiling

…

Try it yourself

Compiling…

Compiled in your browser by Sucrase, the same compiler the live examples on this page use, then formatted with Prettier. The modern transform is the one React 19 expects; the classic one is shown to compare.

One root, because a function returns one value

Here are two tags side by side in a component's return:

Two tags in one return (an error on purpose)Pop out in the code editor (opens in a new tab)JSX
export default function TeaCard() {  return (    <h3>Green tea</h3>    <p>$3</p>  );}
ResultLoading…

The compiler stops on line 4. Picture what it would have to write: each tag is a call, so the return would hold two calls side by side:

What two tags would compile to (not valid JavaScript)Text
return (
  _jsx("h3", { children: "Green tea" })
  _jsx("p", { children: "$3" })
);

Lines 2 and 3 are two values with nothing joining them, and a return gives back exactly one value. There's no JavaScript to write, so the compiler gives up. Different compilers say it differently. Sucrase, in these live examples, says Unterminated regular expression: once the <h3> has ended, it reads the next < as "less than", then tries to read /p> as a regular expression, and gets lost. Babel says it plainly: Adjacent JSX elements must be wrapped in an enclosing tag. Did you want a JSX fragment <>...</>?, and TypeScript says JSX expressions must have one parent element.

Left: return followed by two separate gifts, h3 and p, marked two values, not allowed. Right: return followed by one crate labeled Fragment holding the two gifts h3 and p, marked one value.Two tagsreturnh3ptwo values: not allowedA Fragment around themreturnh3p<>…</>one value
A return gives back one value. Two tags side by side are two values, so the compiler stops (left). A Fragment, <>…</>, holds both in its children, so the component returns one element (right).

The fix is one parent around both. A <div> works, and adds a <div> to the page:

A div around both tagsPop out in the code editor (opens in a new tab)JSX
export default function TeaCard() {  return (    <div>      <h3>Green tea</h3>      <p>$3</p>    </div>  );}
ResultLoading…

Lines 3 and 6 open and close the <div>. When you don't want an extra element, for example inside a list or a table where a <div> doesn't belong, use a Fragment: an empty tag, <> and </>.

A Fragment around both tagsPop out in the code editor (opens in a new tab)JSX
export default function TeaCard() {  return (    <>      <h3>Green tea</h3>      <p>$3</p>    </>  );}
ResultLoading…

The result looks the same, but the page holds only the <h3> and the <p>. Here is what the Fragment compiles to:

The Fragment, compiledJavaScript
import {
  jsx as _jsx,
  Fragment as _Fragment,
  jsxs as _jsxs,
} from "react/jsx-runtime";
export default function TeaCard() {
  return _jsxs(_Fragment, {
    children: [
      _jsx("h3", { children: "Green tea" }),
      _jsx("p", { children: "$3" }),
    ],
  });
}

Line 3 imports Fragment along with jsx and jsxs. Lines 7 to 12 are one call, with React's Fragment as the type and both tags in children: one value for the return. When React meets a Fragment, it puts the children on the page and nothing for the Fragment itself. The long form, <Fragment>…</Fragment> with Fragment imported from react, does the same and can take a key, which the Rendering lists lesson uses.

Every tag must be closed

HTML lets some tags stay open. <br>, <input>, and <img> never have children, and the browser's HTML reader knows each of them by name. It even closes a <li> or a <p> for you when the next one starts. JSX doesn't:

An input left open (an error on purpose)Pop out in the code editor (opens in a new tab)JSX
export default function SignUp() {  return (    <form>      <input type="email">      <button>Join</button>    </form>  );}
ResultLoading…

The <input> on line 4 is never closed, so the compiler takes everything after it as its children: the <button> on line 5, and then </form> on line 6, which doesn't match. Sucrase stops there with Unterminated JSX contents; TypeScript names the real culprit, JSX element 'input' has no corresponding closing tag.

Why so strict? A tag's children go into its call's children, so the compiler must know where they end. And a JSX compiler knows no tag names: <input> and <TeaCard> look alike to it, and either could have children. So you say where every tag ends. A tag with children gets its closing tag, and a tag without children closes itself with />:

export default function SignUp() {  return (    <form>      <p>One email a month.<br />New teas first.</p>      <input type="email" />      <hr />      <button>Join</button>    </form>  );}
ResultLoading…

Line 4 closes the line break as <br />, line 5 the email box, and line 6 a horizontal rule, <hr />. Any tag may close itself when it has no children, which is why you write components as <TeaCard />. Press Edit & run and remove the / on line 6: the result turns into an error, and comes back when you put it back.

Attributes become object keys

You saw className="tea-card" become { className: "tea-card" } in the compiled card. Every attribute becomes a key in the props object, and React DOM uses each key to set the matching DOM property on the real element, the way you would in plain JavaScript with element.className = "tea-card" (the Modifying the DOM lesson covers DOM properties). That's where JSX's attribute names come from: they're the DOM properties' names.

A sign-up form with JSX attribute namesPop out in the code editor (opens in a new tab)JSX
export default function SignUp() {  return (    <form className="join-box">      <label htmlFor="email">Email</label>      <input        id="email"        type="email"        autoComplete="email"        maxLength="40"        aria-describedby="email-hint"      />      <p id="email-hint" className="hint">One email a month.</p>    </form>  );}
ResultLoading…

Line by line:

  • Line 3: className, because the DOM property for class is element.className. The DOM chose that name because class is a reserved word in JavaScript.
  • Line 4: htmlFor, the DOM property for for, which is a reserved word too. Click Email in the result to see it link the label to the box.
  • Lines 8 and 9: multi-word attributes are camelCase, like their DOM properties: autoComplete and maxLength. So are tabIndex, and SVG's strokeWidth for stroke-width. Event props like onClick are camelCase too; Responding to events covers them.
  • Line 10: aria-describedby keeps its dashes. aria-* and data-* attributes are written exactly as in HTML.

There's a JavaScript reason too. You'll often take props out with destructuring, and that makes each name a variable. const { className } = props works, but const { class } = props is a SyntaxError, and a name with a dash, like stroke-width, can't be a variable at all.

Two rows. className="hint" becomes the props key className, which React sets as the DOM property p.className. htmlFor="email" becomes the props key htmlFor, which React sets as label.htmlFor.JSX attributeprops keyDOM propertyclassName="hint"{ className: "hint" }p.classNamehtmlFor="email"{ htmlFor: "email" }label.htmlForthe same name all the way through
An attribute's name becomes a key in the props object, and React DOM sets the DOM property of the same name. The page shows them as the HTML attributes class and for.

What if you write HTML's names anyway? Here the form uses class, for, and tabindex:

HTML's spellings in JSX (a mistake on purpose)Pop out in the code editor (opens in a new tab)JSX
export default function SignUp() {  return (    <form class="join-box">      <label for="email">Email</label>      <input id="email" type="email" tabindex="0" />    </form>  );}
ResultLoading…

The page still looks right: React passes the unknown names to the page as attributes. But press Edit & run and read the console. React warns once for each of lines 3 to 5, and names the fix, as in Invalid DOM property `class`. Did you mean `className`? Treat these warnings as bugs. Pick an attribute below to see how JSX spells it, with live checks of why:

Playground: how JSX spells an attribute
In HTML<p class="hint">One email a month.</p>
In JSX<p className="hint">One email a month.</p>
As a variable namelet class;
DOM property…
HTML spelling in JSXReact warns: Invalid DOM property `class`. Did you mean `className`?
Try it yourself

Write className in JSX. It's the name of the DOM property, p.className.

The variable and property checks run in your browser as you choose. React's warnings come from running each spelling with React.

Turning real HTML into JSX

You'll often start from HTML: a design someone handed you, an old page, a snippet from documentation. Here are a tea shop's opening hours and a visit form, with every kind of difference in it:

Opening hours in HTMLPop out in the code editor (opens in a new tab)HTML
<!-- The shop's opening hours --><h2 class="title">Opening hours</h2><p>Monday to Friday: 8 to 6<br>Saturday: 9 to 4</p><label for="visit">Plan a visit</label><input id="visit" type="date"><label for="name">Your name</label><input id="name" maxlength="20">
ResultLoading…

Turn it into JSX with five checks, in this order:

  1. Wrap it all in one root. Here, a Fragment, <>…</>, since the page doesn't need another element.
  2. Close every tag: the <br> on line 3 and both inputs.
  3. Rename class (line 2) to className and each for (lines 4 and 6) to htmlFor.
  4. Write other multi-word attributes in camelCase: maxlength on line 7 becomes maxLength.
  5. Turn the HTML comment on line 1 into a JavaScript comment inside braces, {/* … */}. JSX has no <!-- -->; the braces, which the next lesson is about, let you put JavaScript, and its comments, in markup.
Opening hours in JSXPop out in the code editor (opens in a new tab)JSX
export default function OpeningHours() {  return (    <>      {/* The shop's opening hours */}      <h2 className="title">Opening hours</h2>      <p>Monday to Friday: 8 to 6<br />Saturday: 9 to 4</p>      <label htmlFor="visit">Plan a visit</label>      <input id="visit" type="date" />      <label htmlFor="name">Your name</label>      <input id="name" maxLength="20" />    </>  );}
ResultLoading…

Line 3 opens the Fragment and line 11 closes it. Lines 4 to 10 are the HTML's seven lines, each fixed. Line 4 is the comment, which never reaches the page. The two results look the same. For a long page, an HTML-to-JSX converter does these steps for you; it still helps to know what it changes and why. One more HTML habit, a style="…" attribute, needs an object in JSX; the next lesson covers it.

How to read this table: each row is an HTML habit, how JSX writes it, and why.

HTML and JSX, side by side
In HTMLIn JSXWhy
Several tags side by sideWrap them in one parent, or in <>…</>.A function returns one value, so a component returns one element.
<br>, <input>, <li> left openClose every tag: <br />, <input />, <li>…</li>.The compiler must know where each tag's children end.
class="hint"className="hint"React uses the DOM property's name, and class is a reserved word in JavaScript.
for="email"htmlFor="email"The DOM property is htmlFor, because for is a reserved word too.
tabindex, maxlength, stroke-widthtabIndex, maxLength, strokeWidthAttributes become object keys, named like DOM properties: camelCase, with no dashes.
aria-label, data-teaThe same: aria-label, data-teaThese two families keep their dashes in JSX.
<!-- a note -->{/* a note */}JSX has no HTML comments. Braces hold a JavaScript comment instead.
Fine in JSX, or needs a fix?
  • <br />
  • <p>Tea<br>Cake</p>
  • <p className="hint">Hi</p>
  • <p class="hint">Hi</p>
  • <button aria-label="Close">x</button>
  • <input tabindex="0" />
  • <><h3>Tea</h3><p>$3</p></>
  • <h3>Tea</h3><p>$3</p>
  • <label htmlFor="email">Email</label>
  • <li data-tea="green">Green tea</li>
Try it yourself
0 of 10 correct

Sort each snippet: would it compile and run with no warning, or does it need a fix?

Choose a category for every card. You can change an answer at any time; Reset clears them all.

Common mix-ups

  • "JSX is HTML inside JavaScript." No. It looks like HTML, but each tag becomes a function call that returns a plain object. That's why its rules are JavaScript's.
  • "The browser runs my JSX." No. A compiler turns it into JavaScript first: your build tool in a project, or Sucrase in this site's live examples.
  • "JSX only works with React." No. JSX is a syntax; React is a library. Other libraries use JSX with their own jsx functions, and React works without JSX, through createElement.
  • "A Fragment adds a wrapper element." No. <>…</> groups tags into one value and leaves nothing on the page.
  • "className is a React invention." No. It's the name of the DOM property, element.className, which existed long before React.
  • "Every attribute is camelCase in JSX." Almost. aria-* and data-* keep their dashes.
  • "Every file with JSX needs import React." Not since the modern transform: the compiler imports jsx from react/jsx-runtime itself.

Practice exercises

Each exercise shows its code's result under it, or when you press Edit & run. When the result is an error, read its line number first. Try a guess, then use the hints.

Exercise 1 · Warm-upCount the calls

How many jsx() or jsxs() calls does this compile to? Use the compiler playground above to check.

Starter codeJSX
const menu = (
  <ul className="menu">
    <li>Green tea</li>
    <li>Mint tea</li>
    <li>Lemon cake</li>
  </ul>
);

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · Warm-upClose the tags

    Press Edit & run: the result is an error. Fix the two open tags. What do you write in place of <br>?

    export default function SignUp() {
      return (
        <form>
          <p>One email a month.<br>New teas first.</p>
          <input type="email">
          <button>Join</button>
        </form>
      );
    }

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · PracticeWrap two tags

      Press Edit & run: two tags side by side stop the compiler. Make the cake card work without adding an element to the page. How many elements does <>…</> add to the page?

      export default function CakeCard() {
        return (
          <h3>Lemon cake</h3>
          <p>$4</p>
        );
      }

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · PracticeRename the attributes

        This feedback form works, but React warns four times. Fix every attribute. What does for become?

        export default function Feedback() {  return (    <form class="join-box">      <label for="note">Tell us what you think</label>      <textarea id="note" maxlength="200" tabindex="0" />      <button>Send</button>    </form>  );}
        ResultLoading…

        Answer, then press Check. Spacing and letter case don’t matter.

          Exercise 5 · ChallengeTurn HTML into a component

          Turn this HTML into the TodaysSpecial component:

          Today's special in HTMLHTML
          <!-- Today's special -->
          <h3 class="special">Today's special</h3>
          <p>Jasmine tea<br>$5 a pot</p>
          <label for="cups">Cups</label>
          <input id="cups" type="number" maxlength="2">

          How many elements does your component put on the page?

          export default function TodaysSpecial() {  // Turn the HTML above into JSX, and return it here  return null;}
          ResultLoading…

          Answer, then press Check. Spacing and letter case don’t matter.

            Exercise 6 · ChallengeWrite JSX's job by hand

            This menu uses no JSX. Add a second list item, Mint tea, by hand. How many list items does the page show now?

            import { createElement } from "react"; export default function Menu() {  return createElement(    "ul",    null,    createElement("li", null, "Green tea"),  );}
            ResultLoading…

            Answer, then press Check. Spacing and letter case don’t matter.

              Check your understanding

              Picture each tag as its jsx() call: most answers follow from it. Press Edit & run on a question's code to check your answer.

              The Writing markup with JSX quiz · 8 questionsScore: first tries count
              1. Question 1 of 8With the modern JSX transform, what does <h3>Green tea</h3> compile to?

                Choose an answer to see the explanation.

              2. Question 2 of 8Why can't a component return two tags side by side, like <h3>…</h3><p>…</p>?

                Choose an answer to see the explanation.

              3. Question 3 of 8What does this component put on the page?

                Read the code, then predictPop out in the code editor (opens in a new tab)JSX
                export default function TeaCard() {
                  return (
                    <>
                      <h3>Green tea</h3>
                      <p>$3</p>
                    </>
                  );
                }

                Choose an answer to see the explanation.

              4. Question 4 of 8Which line is valid JSX?

                Choose an answer to see the explanation.

              5. Question 5 of 8How do you write HTML's <label for="email"> in JSX?

                Choose an answer to see the explanation.

              6. Question 6 of 8Which attribute keeps its dashes in JSX?

                Choose an answer to see the explanation.

              7. Question 7 of 8What does this code print in the console?

                Read the code, then predictPop out in the code editor (opens in a new tab)JSX
                function TeaCard() {
                  return <p>Green tea</p>;
                }
                
                const card = <TeaCard />;
                console.log(typeof card.type);
                
                export default function App() {
                  return card;
                }

                Choose an answer to see the explanation.

              8. Question 8 of 8In a React 19 project, do you need import React from "react" in a file that only uses JSX?

                Choose an answer to see the explanation.

              Key takeaways

              • JSX is markup inside JavaScript. A compiler turns each tag into a jsx() or jsxs() call from react/jsx-runtime, which returns an element: a plain object with a type and props.
              • The classic transform wrote React.createElement and needed import React. React 19 needs the modern one; createElement stays for building elements by hand.
              • A component returns one root, because a function returns one value. Use a Fragment, <>…</>, to add none.
              • Close every tag, <br /> and <input /> too: the compiler must know where children end.
              • Attributes become props keys named like DOM properties: className, htmlFor, camelCase. Only aria-* and data-* keep their dashes.

              Remember the one-liner.
              Every JSX tag is a function call, so JSX follows JavaScript's rules, not HTML's.

              Coming next: JavaScript in JSX with curly braces. It shows how to put values, expressions, and objects like style inside your markup.

              CompleteFrontend Clear concepts. Working examples.