JSX looks like HTML but compiles down to plain JavaScript function calls, which is why it follows a stricter, JavaScript-driven set of rules. This lesson walks through those rules ā from embedding expressions with curly braces to camelCase attributes, Fragments, and conditional rendering ā that every React component's markup depends on.
1Welcome to JSX
JSX stands for JavaScript XML ā a syntax extension for JavaScript that lets you write HTML-like markup directly inside your JS files. It looks exactly like HTML at a glance, but it's actually much stricter and carries dynamic capabilities that plain HTML doesn't have.
JSX isn't strictly required to use React, but it's the overwhelmingly recommended approach because it gives component code a clear, visual structure that's easy to read at a glance.
const element = <h1>Hello JSX</h1>;Hello JSX
2The Compilation Secret
Browsers don't natively understand JSX ā running it directly in a JavaScript engine would throw a syntax error immediately. Before code reaches the browser, build tools like Babel compile JSX into standard JavaScript, transforming tags into React.createElement() calls.
This is why, in older versions of React, React had to be explicitly in scope wherever JSX was used: the compiled output referenced React.createElement directly, even though the source code never mentioned it.
// JSX: <h1>Hi</h1>
// JS: React.createElement('h1', null, 'Hi');Hi
3Embedding Expressions
Curly braces {} create a window that escapes out of JSX markup back into plain JavaScript. Inside them, you can place any valid expression ā a variable name, a mathematical calculation, or a function call like name.toUpperCase() ā and React evaluates it and injects the result into the DOM.
This is the mechanism that makes JSX dynamic instead of static: whatever the expression resolves to at render time is exactly what appears on screen.
const name = 'Lolly';
return <h1>Welcome, {name.toUpperCase()}!</h1>;Welcome, LOLLY!
4Reserved Keywords
Because JSX is closer to JavaScript than HTML, it must follow JavaScript's rules about reserved keywords. The most common pitfall is the class attribute ā class is a reserved word in JavaScript for defining ES6 classes, so JSX cannot use it directly.
To apply CSS classes in JSX, you must use className instead, exactly as shown in <div className="container">. Forgetting this and writing class won't crash the app, but the styling simply won't apply.
<div className='container'>
<p>Styled with CSS</p>
</div>Styled with CSS
5camelCase Attributes
Building on the reserved-word rule, React requires all multi-word HTML attributes and event handlers to be written in camelCase. onclick becomes onClick, tabindex becomes tabIndex, and the for attribute on labels ā also a reserved JS loop keyword ā becomes htmlFor.
This naming consistency mirrors how the DOM APIs themselves are exposed to JavaScript, so it isn't an arbitrary React convention but a bridge to how the underlying platform already names these properties.
<button ???={() => {}}>Click Me</button>6The Root Element Rule
A JSX expression must evaluate to exactly one root element ā you can't return an <h1> and a <p> side by side from a component without a wrapper. Wrapping them in an extra <div> works, but it adds a node to the DOM that has no real purpose.
React's Fragment, written as empty tags <></>, solves this by grouping elements to satisfy the single-root rule without injecting an unnecessary wrapper element into the rendered output.
<label htmlFor='email'>Email:</label>
<input id='email' onClick={() => {}} />Header
Body text directly adjacent in DOM
7Conditionals: Ternary
You can't use a traditional if/else statement directly inside a JSX block, because JSX expects an expression ā a value ā not a statement. The ternary operator (condition ? trueResult : falseResult) fills this gap for inline conditional rendering.
A pattern like {isLoggedIn ? <Logout /> : <Login />} is extremely common in React precisely because it lets a single expression decide which of two elements to render based on a boolean.
<>
<h1>Header</h1>
<p>Body</p>
</>8Conditionals: Logical AND
When you want an element to render only if a condition is true, and render nothing at all otherwise, the logical AND operator (&&) is the tool for the job. React evaluates the left side, and if it's truthy, renders whatever's on the right side of the &&.
If the condition is falsy, React ignores the right side entirely, so a pattern like {hasNewMessages && <Notification />} leaves the DOM clean rather than rendering an empty or broken element.
<div>
{isLoggedIn ? <Logout /> : <Login />}
{hasNewMessages && <Notification />}
</div>9Comments in JSX
Standard HTML comments (<!-- -->) cause syntax errors inside JSX, and standard JavaScript // comments placed between tags will render directly to the screen as literal text instead of being hidden.
To comment inside a JSX tree, you first open a JavaScript expression window with curly braces, then place a block comment inside it: {/* comment */}. That's the only form of comment JSX actually recognizes as non-rendering.
Result: ???Content
(Comment does not render to DOM)
10Automatic Escaping Prevents Injection
Every value embedded with curly braces is automatically escaped by React before it touches the DOM. A comment or username containing <script>alert(1)</script> rendered as {comment.text} shows up as harmless, literal text ā it is never parsed as an actual tag.
This is what protects a React app against XSS by default, without writing a single line of manual sanitization. The one deliberate escape hatch is dangerouslySetInnerHTML, whose intentionally alarming name is a reminder to only ever use it with content that's already been sanitized by a trusted library.
const comment = { text: '<script>alert(1)</script>' };
return <p>{comment.text}</p>;11Mastery Achieved
Every curly-brace expression in JSX is evaluated, not printed as text ā <h1>{10 + 5}</h1> renders the number 15, not the literal string 10 + 5. That evaluation behavior is what ties together everything from embedded variables to ternaries to logical AND checks.
Keeping that mental model ā curly braces mean 'evaluate this JavaScript and show the result' ā is what makes the rest of JSX's syntax rules feel consistent rather than arbitrary.
<span> { isAdmin ? 'Admin' : '???' } </span>JSX Complete ā
12Mastery Achieved
The naming rules in this lesson ā className instead of class, htmlFor instead of for, and camelCase for every multi-word attribute ā all trace back to the same root cause: JSX attributes are JavaScript property names, not HTML attribute strings.
Once that connection clicks, the exceptions stop feeling like a list to memorize and start feeling like a predictable consequence of JSX being JavaScript underneath its HTML-like appearance.
<div>
{/* This is a JSX comment */}
<h1>Content</h1>
</div>JSX Complete ā
13Mastery Achieved
Structurally, this lesson also covered how JSX keeps components clean: the single-root-element rule forces every component to return one coherent tree, and Fragments let you satisfy that rule without littering the DOM with meaningless wrapper <div>s.
Combined with conditional rendering via ternaries and &&, this is what lets a single component's markup adapt to different states while still compiling down to one predictable, minimal DOM structure.
/* JSX Architecture Rendered */JSX Complete ā
14Mastery Achieved
With JSX syntax mastered ā compilation to React.createElement, curly-brace expressions, camelCase and reserved-keyword exceptions, Fragments, and conditional rendering ā you have everything needed to read and write real component markup fluently.
The next step is learning how components pass data to one another through props, building on this same JSX foundation to create genuinely reusable pieces of UI.
/* Next: Prop Flow */JSX Complete ā
15Step-by-Step Breakdown
Welcome to JSX. Welcome to JSX Architecture. JSX stands for JavaScript XML. It is a syntax extension for JavaScript that allows you to write HTML-like markup directly inside your JavaScript files. While it may look exactly like HTML, it is actually much stricter and has dynamic capabilities. JSX is not strictly required to use React, but it is highly recommended because it provides a clear, visual structure to your component code.
The Compilation Secret. Browsers don't natively understand JSX. If you tried to run JSX in a normal JavaScript engine, it would throw a syntax error immediately. Before your code reaches the browser, build tools like Babel compile your JSX into standard JavaScript function calls. Specifically, it transforms your tags into React.createElement() invocations. This is why React must always be in scope when using JSX in older versions of React.
Embedding Expressions. The true power of JSX lies in its ability to embed JavaScript directly within the markup. By using curly braces {}, you create a window that escapes out of HTML syntax and back into JavaScript. Inside these braces, you can place any valid JavaScript expression, such as variable names, mathematical calculations, or function calls. React will evaluate the expression and inject the result into the DOM.
To execute mathematical calculations dynamically inside your markup, which syntax is required to escape the JSX scope?
- ā( 10 + 5 ) Parentheses
- ā{ 10 + 5 } Curly Braces
Reserved Keywords. Because JSX is closer to JavaScript than it is to HTML, it must adhere to JavaScript's strict rules regarding reserved keywords. The most common pitfall for beginners is the 'class' attribute. In JavaScript, 'class' is a reserved word used for defining ES6 classes. Therefore, to apply CSS classes in JSX, you must strictly use the 'className' attribute instead.
In standard HTML, you bind an inline click handler using the 'onclick' attribute. How must this be written in React JSX?
- āonclick
- āonClick (camelCase)
camelCase Attributes. Building upon the reserved word rule, React dictates that all HTML attributes and event handlers must be written in camelCase. This means 'onclick' becomes 'onClick', 'tabindex' becomes 'tabIndex', and the 'for' attribute in labels (which is also a reserved JS loop keyword) becomes 'htmlFor'. This strict naming consistency helps bridge the gap between DOM APIs and JavaScript logic.
The Root Element Rule. A fundamental limitation of JSX is that an expression must evaluate to a single root element. You cannot return multiple parallel elements (like an h1 and a p tag side-by-side) from a component without wrapping them in a parent. If you do not want to add unnecessary div wrappers that bloat your DOM tree, React provides a feature called 'Fragments', denoted by empty tags: <></>.
Conditionals: Ternary. You cannot use traditional if/else statements directly inside a JSX block because JSX expects an expression (a value), not a statement. Instead, you must use the JavaScript ternary operator (condition ? trueResult : falseResult) for inline conditional rendering. This pattern is incredibly common in React for toggling UI states based on boolean variables.
Conditionals: Logical AND. When you only want to render an element if a condition is true, and render absolutely nothing if it is false, you can use the Logical AND (&&) operator. React will evaluate the condition, and if it is truthy, it will render the JSX on the right side of the &&. If it is falsy, React will ignore it entirely, leaving the DOM clean.
Because curly braces execute JavaScript inside JSX, what will physically render to the screen when React processes <h1>{ 10 + 5 }</h1>?
- āThe string '10 + 5'
- āThe evaluated number '15'
Complete the following ternary logic to render 'Admin Dashboard' if the isAdmin boolean is true, and 'User Profile' if it is false.
- āUser Profile
- āAdmin Dashboard
If a component attempts to return both an <h1> and a <p> element at the same hierarchy level, how should you wrap them to prevent a compiler error?
- āUse a <div> to bloat the DOM
- āUse a React Fragment <>
Comments in JSX. Standard HTML comments <!-- --> will cause syntax errors in JSX. Standard JavaScript comments // will render directly to the screen as text if placed between tags. To write a comment inside a JSX tree, you must first open a JavaScript expression window using curly braces, and then place a multi-line JavaScript block comment inside it: {/* comment */}.
Automatic Escaping Prevents Injection. Every value you embed with curly braces is automatically escaped by React before it touches the DOM. If a comment or username contains something like '<script>alert(1)</script>', rendering it as {comment.text} shows that string as harmless, literal text on the page ā it is never parsed as an actual tag. This is what protects a React app against XSS by default, without you writing a single line of sanitization code.
Mastery Achieved. JSX Syntax Mastery achieved! You understand how JSX compiles to JS, how to use camelCase, embed expressions, manage conditionals, and utilize Fragments to keep your DOM clean. You are fully ready to leverage these structures inside powerful React components.
Level Up š
Advanced cheat sheets, SEO tricks, and interview prep for this topic.
Browser Support
Fully supported.
Fully supported.
Fully supported.
Fully supported.
Accessibility (A11y)
1Conditional Rendering Should Not Silently Remove Announced Content
When `{condition && <Element />}` toggles a piece of UI, make sure the removal or appearance of that content is also communicated to assistive technology (for example via `aria-live`) if it changes in response to something other than a full page navigation.
2Fragments Are Invisible to Both the DOM and Accessibility Tree
Because `<>...</>` renders no actual element, it cannot carry a `role`, `aria-label`, or any other accessibility attribute ā if a wrapper needs semantics, use a real element like `<section>` instead of a Fragment.
SEO Implications
- 1
JSX Comments Never Reach the Rendered Output
Because `{/* comment */}` compiles away entirely, JSX comments have zero effect on the HTML a crawler sees ā they're purely a developer-facing tool and carry no SEO weight in either direction.
- 2
Conditionally Rendered Content Depends on What's True at Render Time
Content behind a ternary or `&&` check is only present in the markup when its condition evaluates to true at the moment of rendering ā if that condition depends on client-side-only data, a crawler evaluating server-rendered HTML may see a different result than a user's browser does.
Best Practices
Prefer the Logical AND Operator Only for True Show/Hide Cases
`{count && <Badge />}` can accidentally render the literal number `0` to the screen if `count` is `0`, since `0` is falsy but not `null`/`undefined`. Use an explicit boolean check like `{count > 0 && <Badge />}` instead.
Keep Ternaries Simple; Extract Complex Conditional Logic
A short `condition ? <A /> : <B />` is readable inline, but nested or chained ternaries inside JSX quickly become hard to follow ā move complex branching into a helper function or an early return above the JSX instead.
Frequent Bugs
A component using `{someArray.length && <List />}` unexpectedly renders a stray 0 on the page when the array is empty.
The logical AND operator renders whatever the left-hand expression evaluates to when it's falsy but not boolean ā `0` gets rendered as text. Convert the condition to an explicit boolean, e.g. `{someArray.length > 0 && <List />}`.
Adding a second top-level element to a component's return statement causes a build-time syntax error.
JSX requires a single root element per component; wrap the sibling elements in a Fragment (`<>...</>`) or a single parent element instead of returning them side by side.
Real-World Examples
Toggling a Notification Badge With Logical AND
A navbar renders an unread-messages badge only when there are actually unread messages, keeping the DOM free of an empty badge element the rest of the time.
return (
<>
{hasNewMessages && <Notification count={unreadCount} />}
</>
);