Topics in this subject
React 1 min read Updated 5 Aug 2026

JSX

JSX is syntactic sugar over React.createElement — expressions in braces, camelCase props, one root, no inline if.

JSX is sugar for React.createElement#

JSX is not HTML and not part of JS. A compiler (Babel / SWC / TS) transforms it into React.createElement calls (React 17+ uses the automatic jsx runtime, but the mental model is the same). 🎯

// You write:
const el = <button className="primary" onClick={handleClick}>Save</button>;

// Compiles to (classic runtime):
const el = React.createElement(
  "button",
  { className: "primary", onClick: handleClick },
  "Save"
);

Because JSX is just function calls returning objects (React elements), it's a first-class value: assign it to variables, return it, store it in arrays.

Expressions in {}#

Anything between braces is a JS expression whose value is embedded:

<h1>Hello, {user.name.toUpperCase()}</h1>
<p>Total: {price * qty}</p>
<img src={avatarUrl} alt={`${user.name}'s avatar`} />

⚠️ Only expressions — things that evaluate to a value. Statements (if, for, switch) are not allowed inline because createElement takes argument values, and a statement has no value.

// ❌ Invalid — `if` is a statement, not an expression
<div>{if (loggedIn) { ... }}</div>

// ✅ Use ternary or && (expressions)
<div>{loggedIn ? <Dashboard /> : <Login />}</div>
<div>{error && <Alert msg={error} />}</div>

Attribute differences 🎯#

JSX props map to DOM properties, not HTML attributes — hence camelCase and a few renames.

HTML JSX Reason
class className class is a JS reserved word
for htmlFor for is a JS reserved word
onclick onClick camelCase synthetic events
tabindex tabIndex DOM property naming
style="color:red" style={{ color: "red" }} object, camelCase keys

One root element / Fragments#

A component must return a single root node (one createElement call). Wrap siblings in a Fragment to avoid an extra DOM wrapper. 🟢

// ❌ Two roots
return <A /><B />;

// ✅ Fragment shorthand — no extra DOM node
return <><A /><B /></>;

// Use <React.Fragment key={...}> when you need a key (e.g. in lists)

Conditionals & lists inline#

function Items({ items, loading }) {
  return (
    <ul>
      {loading && <li>Loading…</li>}
      {items.map(it => <li key={it.id}>{it.label}</li>)}
      {items.length === 0 ? <li>Empty</li> : null}
    </ul>
  );
}

What renders nothing ⚠️#

false, null, undefined, and true render nothing. Whitespace-only strings are ignored. But 0 renders as "0" — a classic bug:

{items.length && <List />}   // ❌ renders "0" when array is empty
{items.length > 0 && <List />} // ✅ coerce to boolean first

JSX → element → DOM#

flowchart LR
  JSX["JSX source"] -->|"Babel / SWC"| CE["React.createElement()"]
  CE --> EL["React element (plain JS object)"]
  EL -->|"render + reconcile"| DOM["Real DOM nodes"]

Interview Q&A#

Q1. What does JSX compile to? React.createElement(type, props, ...children) calls (or the automatic jsx() runtime in React 17+), which return plain JS objects called React elements.

Q2. Why className instead of class? class is a reserved keyword in JavaScript. JSX props map to DOM properties, so React uses className (and htmlFor for for).

Q3. Why can't you use an if statement inside JSX braces? Braces accept expressions (values passed to createElement), and if is a statement with no value. Use ternaries or && instead.

Q4. Why does {count && <X/>} sometimes render a stray 0? When count is 0, && returns 0, which React renders as text. Use an explicit boolean like count > 0 && <X/>.

Q5. How do you return multiple elements without an extra DOM wrapper? Wrap them in a Fragment <>…</> (or <React.Fragment>), which groups siblings without adding a DOM node.