JSX in depth
Expressions in braces, attributes, fragments, styles, comments and how JSX compiles.
JSX is the HTML-like syntax you write inside React components. It looks like HTML, but it's really JavaScript — which gives you the full power of the language for building UI. In this lesson you'll learn exactly how JSX works and the rules that differ from HTML.
JSX is JavaScript#
Your build tool (Vite) compiles JSX into ordinary function calls. This:
becomes roughly:
The result is a plain object (a React element) describing what to render. That's why JSX can be stored in variables, passed to functions, put in arrays and returned from components — it's just a value.
Embedding expressions with { }#
Curly braces switch from markup back to JavaScript. Anything that produces a value works:
- Text content:
{user.born}. - Attribute values:
src={user.avatar}— no quotes around the braces. - Template literals work inside braces:
alt={`Portrait of ${user.first}`}.
What renders and what doesn't
Plain objects can't be rendered: {user} throws "Objects are not valid as a React child". Render a property ({user.first}) or JSON.stringify(user) for debugging.
Statements aren't allowed inside braces
if, for and const are statements, not expressions. Compute values before the return, or use expression forms:
Conditional rendering and lists each get their own lesson.
Attributes: the differences from HTML#
JSX attributes follow JavaScript (DOM property) naming:
Most attributes are camelCase (onChange, autoComplete, maxLength, readOnly). Two exceptions keep their dashes: aria-* and data-*:
Strings vs expressions
Quotes pass a string; braces pass any JavaScript value (numbers, booleans, objects, functions).
Inline styles
The double braces aren't special syntax: the outer pair means "JavaScript here", the inner pair is an object literal. CSS properties are camelCase (backgroundColor, not background-color). For most styling, prefer CSS classes — see the Styling lesson.
Boolean and spread attributes
{...rest} spreads every remaining prop onto the element — handy for wrapper components.
One root element: fragments#
A component returns one value, so sibling elements need a wrapper:
<>...</> is shorthand for <Fragment>. Use the long form when you need a key, e.g. in lists: <Fragment key={item.id}>.
Close every tag#
In JSX, every element must be closed — including "void" HTML elements:
Multi-line JSX: use parentheses#
Without the parentheses, a line break right after return triggers automatic semicolon insertion and the function returns undefined.
Comments#
Whitespace#
JSX trims whitespace at the start and end of lines and removes line breaks between elements:
Use {" "} for an explicit space; Prettier inserts it automatically.
JSX is safe by default#
Values in braces are escaped, so user input can't inject HTML or scripts:
The escape hatch, dangerouslySetInnerHTML={{ __html: html }}, is named to scare you. Only use it with trusted or sanitised HTML (e.g. via DOMPurify).
Elements as values#
Because JSX is a value, you can store and pass it around:
Common mistakes#
- Writing
classinstead ofclassNameorforinstead ofhtmlFor(React warns in the console). - Putting quotes around braces:
src="{url}"passes the literal string{url}. - Rendering an object:
{user}. - Forgetting to close tags:
<img>,<br>. - Using
style="..."with a string instead of an object. returnon its own line followed by JSX without parentheses.
What's next#
Next, make components reusable by passing them data with props.
Check your understanding
Quick quiz
1.Which of these can you put inside
{ }in JSX?2.How do you set an inline style in JSX?
3.Why does a component need to return a single root element (or fragment)?
Finished reading?
Mark this lesson complete to track your progress.