JSX in Depth
What JSX compiles to, expressions in braces, and the rules that trip people up.
What you'll learn
- That JSX is a function call in disguise
- How to put values and expressions into markup
- The attribute names that differ from HTML, and why
- Fragments, self-closing tags, and comments
JSX is not HTML
JSX looks like markup, but it is JavaScript. A build step turns every tag into a function call. These two are the same thing:
const element = <p className="intro">Hello</p>;
// compiles to roughly:
const element = jsx("p", { className: "intro", children: "Hello" });
That is the whole trick, and two useful facts follow from it.
First, a JSX expression is a value. You can store it in a variable, put it in an array, return it from a function, or pass it as an argument:
Second, it is not a string. There is no HTML parsing at runtime, and values
you interpolate are escaped, so JSX does not expose you to injection the way
innerHTML does.
Braces take an expression
Curly braces switch from markup back into JavaScript. Anything that evaluates to a value is allowed:
The word expression is doing real work there. An expression produces a value;
a statement does not. So if, for, and switch cannot go inside braces —
you either compute the value before the return, or use a ternary, which is
covered in Conditional Rendering.
Attributes take expressions too
Use braces instead of quotes to pass a value rather than a literal string:
Note style takes an object, not a string — which is why it has two sets of
braces: one to enter JavaScript, one for the object literal. Its properties are
camelCased (fontSize, not font-size).
Attribute names that differ
JSX attributes are named after DOM properties, not HTML attributes. The ones you will hit immediately:
| HTML | JSX |
| --- | --- |
| class | className |
| for | htmlFor |
| tabindex | tabIndex |
| onclick | onClick |
class and for are reserved words in JavaScript, which is the reason for the
first two. The rest is a consistent rule: multi-word attributes are camelCased.
One parent element
A component returns one value, so JSX must have a single root. Two adjacent elements are a syntax error.
When you do not want a wrapper element in the output, use a fragment —
written <>...</>:
That matters for layout: an extra <div> inside a grid or flex container can
break it. A fragment leaves no trace in the DOM.
Closing tags and comments
Every element must be closed. Tags with no children close themselves — <br />,
<img />, <input /> — including your own components: <Greeting />.
Comments inside JSX go in braces, because they are JavaScript:
<div>
{/* This is a comment in JSX */}
<p>Visible</p>
</div>
Common mistakes
- Using
class. It isclassName. - Returning two elements. Wrap them in a fragment.
- Putting an
ifin braces. Braces take an expression; compute first. style="color: red".styletakes an object:style={{ color: "red" }}.- Forgetting to close a tag.
<img>must be<img />.
Exercise
This component renders, but React is complaining in the console about two
attribute mistakes: it uses class, and its style key is written the CSS way
rather than the JavaScript way.
Fix both. Then split the greeting into an <h1> for the heading and a <p> for
the sentence — without adding a wrapper element, which is what fragments are
for.