Lesson 2 of 31
JSX and Elements
How JSX compiles to plain JavaScript function calls, the handful of rules that trip up newcomers, and what a React element actually is under the hood.
You've now seen JSX in passing — the <button>...</button>-looking syntax
inside a component. This lesson is about what it actually is, because
"HTML inside JavaScript" is a useful mental model right up until it misleads
you.
JSX is not HTML
JSX looks like markup, but it is not interpreted by anything at runtime. A build step — Babel, SWC, or the TypeScript compiler — transforms every JSX tag into a plain JavaScript function call before your code ever ships. This:
const element = <h1 className="title">Hello</h1>;compiles to something equivalent to this:
const element = React.createElement("h1", { className: "title" }, "Hello");(Modern tooling actually emits a call to an internal jsx function imported
automatically from react/jsx-runtime, which is a small optimization over
calling React.createElement directly — but the idea is identical: JSX is
sugar for function calls, nothing more.)
That function call returns a plain JavaScript object — a React element — describing what should appear on screen: what kind of tag, what props, what children. It is not a real DOM node. It's more like a lightweight description, cheap to create and throw away, which React later uses to figure out what real DOM nodes to create or update. That's the whole basis for the "virtual DOM" idea you'll go deeper on in a later lesson.
Expressions live inside curly braces
Anywhere you want a JavaScript value inside JSX, wrap it in {}:
function Greeting({ name }) {
const upper = name.toUpperCase();
return <p>Hello, {upper}! Today is {new Date().toLocaleDateString()}.</p>;
}Anything that is a valid JavaScript expression is allowed inside {} —
variables, function calls, ternaries, arithmetic. Statements (if, for,
variable declarations) are not expressions, so they can't go directly inside
{}; that's why conditional rendering in JSX leans on expressions like the
ternary operator or &&, which the next lesson covers properly.
One root value
A component has to return one value, and since JSX compiles to one function call per tag, that means one root element:
// Invalid — two sibling elements with no common parent
function Card() {
return (
<h2>Title</h2>
<p>Body</p>
);
}Wrapping in a div fixes it, but adds a real DOM node you might not want.
The fragment (<>...</> or the explicit <Fragment>...</Fragment>)
groups children without adding anything to the DOM at all:
function Card() {
return (
<>
<h2>Title</h2>
<p>Body</p>
</>
);
}The handful of naming differences
Because JSX attributes become JavaScript object properties, a few HTML attribute names change to avoid colliding with reserved words or existing DOM API names:
classbecomesclassNamefor(on alabel) becomeshtmlFortabindexbecomestabIndex— JSX attributes are camelCase in general, matching the DOM property names rather than the HTML attribute names- Inline styles are an object, not a string:
style={{ color: "red" }}(the outer{}is "this is an expression", the inner{}is the object literal itself)
Everything else reads close enough to HTML that it rarely trips people up.
Self-closing tags are mandatory for empty elements
In HTML, <img src="..."> and <br> don't need a closing slash. In JSX,
every element must be explicitly closed, so an element with no children
needs the self-closing form:
<img src="/cat.png" alt="A cat" />
<br />Leaving off the slash on an empty tag is a syntax error, not a lenient warning — the JSX compiler enforces it.
Try it yourself
This component uses an expression, a conditional, and a fragment, all in one small example:
What to remember
- JSX compiles to plain JavaScript function calls — there's no runtime magic, and no JSX left once it's compiled.
- A React element is a lightweight object describing UI, not a real DOM node.
{}switches into JavaScript-expression mode; only expressions are allowed, not statements.- A component returns one root value — use a fragment (
<>...</>) to group siblings without adding a DOM node. - Attribute names are camelCase and match DOM properties (
className,htmlFor,onClick), not raw HTML attribute names.
Check yourself
4 questions · pass 3/4 to unlock Components and Props
1.What does JSX actually compile down to?
2.Why does
return <div><p>One</p><p>Two</p></div>;need the outerdiv(or a fragment)?3.Which is the correct way to render a dynamic value inside JSX?
4.Why is
className, notclass, the correct JSX attribute for a CSS class?
4 left to answer