React Interview Prep
React Core Concepts

JSX

Not HTML: What React Actually Compiles and Why It Matters

LinkedIn Hook

You write <div class="container"> in a React component.

The app renders. No error. But your styles don't apply.

You spend 20 minutes debugging CSS before realizing — React uses className, not class.

Here's the thing: JSX looks like HTML, but it is NOT HTML. It's syntactic sugar for JavaScript function calls. Every <div> you write gets compiled to React.createElement("div", ...) — and that distinction changes everything.

In this lesson, I break down what JSX actually compiles to, the rules that trip up every developer (className, htmlFor, camelCase), how to embed expressions, the 3 patterns for conditional rendering, and why the key prop exists — and what happens when you get it wrong.

If an interviewer asks "what is JSX?" and you answer "it's basically HTML" — you've already lost points.

Read the full lesson → [link]

#React #JavaScript #WebDevelopment #InterviewPrep #Frontend #CodingInterview #JSX #100DaysOfCode


JSX thumbnail


What You'll Learn

  • What JSX actually compiles to under the hood (React.createElement)
  • The JSX rules that differ from HTML (className, htmlFor, camelCase attributes)
  • How to embed JavaScript expressions inside JSX
  • Three conditional rendering patterns and when to use each
  • Why the key prop matters in lists and what goes wrong without it

JSX Is a Translation Layer, Not a Template Language

The Analogy

Imagine you're writing a letter in shorthand. You write quick, abbreviated notes that make sense to you — but before the letter gets mailed, a translator converts every shorthand symbol into proper, full English sentences.

JSX works the same way. You write <h1>Hello</h1> — that's the shorthand. But React never sends that to the browser. A compiler (Babel) translates your shorthand into React.createElement("h1", null, "Hello") — the full JavaScript that React actually executes.

HTML lives in .html files and the browser parses it directly. JSX lives in .js files and must be compiled before the browser ever sees it. That single difference is why JSX has its own rules.

What JSX Compiles To

Every piece of JSX is a React.createElement() call. Understanding this removes all the mystery.

// What you write (JSX)
const element = <h1 className="title">Hello, World!</h1>;

// What Babel compiles it to (JavaScript)
const element = React.createElement(
  "h1",                        // type: the HTML tag or component
  { className: "title" },      // props: an object of attributes
  "Hello, World!"              // children: content inside the tag
);

// What React.createElement returns (a plain object)
// {
//   type: "h1",
//   props: {
//     className: "title",
//     children: "Hello, World!"
//   }
// }

Nested elements become nested calls:

// JSX
const app = (
  <div>
    <h1>Title</h1>
    <p>Paragraph</p>
  </div>
);

// Compiles to
const app = React.createElement(
  "div",
  null,
  React.createElement("h1", null, "Title"),
  React.createElement("p", null, "Paragraph")
);

This is why JSX must have a single parent element — React.createElement takes one type as its first argument. You can't return two sibling calls without wrapping them.

Note: Since React 17, the new JSX transform imports jsx from react/jsx-runtime automatically — you no longer need import React from "react" at the top of every file. But the concept is identical: JSX still compiles to function calls that produce plain objects.


JSX Rules — Where HTML Habits Break

Since JSX is JavaScript, not HTML, certain HTML attributes collide with JavaScript reserved words or conventions. Here are the rules you must know.

className, Not class

// WRONG — "class" is a reserved word in JavaScript
<div class="container">Hello</div>

// CORRECT — use className
<div className="container">Hello</div>

htmlFor, Not for

// WRONG — "for" is a reserved word in JavaScript (for loops)
<label for="email">Email</label>

// CORRECT — use htmlFor
<label htmlFor="email">Email</label>
<input id="email" type="email" />

camelCase for All Attributes

HTML attributes are case-insensitive. JSX attributes are JavaScript object keys — they follow camelCase.

// HTML style                    // JSX equivalent
// onclick="handleClick()"    → onClick={handleClick}
// tabindex="0"               → tabIndex={0}
// maxlength="100"            → maxLength={100}
// readonly                   → readOnly
// autofocus                  → autoFocus
// contenteditable            → contentEditable

style Takes an Object, Not a String

// HTML
// <div style="background-color: red; font-size: 16px;">

// JSX — double curly braces: outer = expression, inner = object
<div style={{ backgroundColor: "red", fontSize: "16px" }}>
  Styled content
</div>
// CSS property names become camelCase: background-color → backgroundColor

Self-Closing Tags Are Required

In HTML, <img>, <br>, <input> can be left unclosed. In JSX, every tag must close.

// WRONG in JSX
<img src="photo.jpg">
<br>
<input type="text">

// CORRECT in JSX
<img src="photo.jpg" />
<br />
<input type="text" />

Boolean Attributes

// In HTML, presence alone means true: <input disabled>
// In JSX, you can do the same or be explicit:
<input disabled />          // disabled = true (implicit)
<input disabled={true} />   // same thing, explicit
<input disabled={false} />  // NOT disabled

JSX visual 1


Expressions in JSX

JSX lets you embed any JavaScript expression inside curly braces {}. An expression is anything that produces a value — a variable, a function call, a math operation, a ternary.

const name = "Rakibul";
const age = 25;

function ProfileCard() {
  return (
    <div>
      {/* Variable */}
      <h1>{name}</h1>

      {/* Expression */}
      <p>Next year: {age + 1}</p>

      {/* Function call */}
      <p>{name.toUpperCase()}</p>

      {/* Ternary */}
      <p>{age >= 18 ? "Adult" : "Minor"}</p>

      {/* Template literal */}
      <p>{`Hello, ${name}!`}</p>
    </div>
  );
}

// Output:
// <h1>Rakibul</h1>
// <p>Next year: 26</p>
// <p>RAKIBUL</p>
// <p>Adult</p>
// <p>Hello, Rakibul!</p>

What You Cannot Put in Curly Braces

Statements do not produce values — you cannot use if, for, while, or switch directly inside JSX.

// WRONG — if is a statement, not an expression
<div>
  {if (loggedIn) { return "Welcome" }}
</div>

// CORRECT — use a ternary (expression) or move the logic outside
<div>
  {loggedIn ? "Welcome" : "Please log in"}
</div>

Rendering Arrays

JSX can render arrays of elements. This is how lists work.

const fruits = ["Apple", "Banana", "Cherry"];

function FruitList() {
  return (
    <ul>
      {fruits.map((fruit, index) => (
        <li key={fruit}>{fruit}</li>
      ))}
    </ul>
  );
}

// Output:
// <ul>
//   <li>Apple</li>
//   <li>Banana</li>
//   <li>Cherry</li>
// </ul>

Values JSX Ignores

React silently skips certain values — this is a feature, not a bug. It enables conditional rendering patterns.

<div>
  {true}       {/* renders nothing */}
  {false}      {/* renders nothing */}
  {null}       {/* renders nothing */}
  {undefined}  {/* renders nothing */}
  {"hello"}    {/* renders "hello" */}
  {0}          {/* renders "0" — this catches people off guard! */}
  {""}         {/* renders nothing */}
</div>

The fact that 0 renders but false does not is the source of a very common bug. We'll see it in the next section.


Conditional Rendering Patterns

Since you can't use if statements inside JSX, React developers use three patterns for conditional rendering. Interviewers love asking about the differences.

Pattern 1: Ternary Operator (if-else)

Use when you need to render one thing OR another.

function Greeting({ isLoggedIn }) {
  return (
    <div>
      {isLoggedIn ? (
        <h1>Welcome back!</h1>
      ) : (
        <h1>Please sign in.</h1>
      )}
    </div>
  );
}

// isLoggedIn = true  → "Welcome back!"
// isLoggedIn = false → "Please sign in."

Pattern 2: Logical AND (&&) — Show or Hide

Use when you want to render something OR nothing.

function Notification({ hasMessages, count }) {
  return (
    <div>
      {hasMessages && <span>You have new messages!</span>}
    </div>
  );
}

// hasMessages = true  → renders the span
// hasMessages = false → renders nothing (false is ignored by JSX)

The && trap with numbers — an interview favorite:

function MessageCount({ count }) {
  return (
    <div>
      {/* BUG: when count is 0, this renders "0" on screen! */}
      {count && <span>You have {count} messages</span>}

      {/* FIX: convert to boolean first */}
      {count > 0 && <span>You have {count} messages</span>}

      {/* Or use Boolean() */}
      {Boolean(count) && <span>You have {count} messages</span>}

      {/* Or use double negation */}
      {!!count && <span>You have {count} messages</span>}
    </div>
  );
}

// count = 5 → renders "You have 5 messages"
// count = 0 → BUG line renders "0", FIX lines render nothing

Why does this happen? The && operator returns the first falsy value. When count is 0, 0 && <span>... returns 0 — and React renders the number 0 on screen. It does NOT render false, null, or undefined, but it DOES render 0.

Pattern 3: Early Return

Use when an entire component should not render, or when you want to handle edge cases before the main return.

function Dashboard({ user, isLoading }) {
  // Early return for loading state
  if (isLoading) {
    return <p>Loading...</p>;
  }

  // Early return for no user
  if (!user) {
    return <p>Please log in to view dashboard.</p>;
  }

  // Main render — only reached if loaded and user exists
  return (
    <div>
      <h1>Welcome, {user.name}</h1>
      <p>Email: {user.email}</p>
    </div>
  );
}

Early return keeps the main JSX clean — no deeply nested ternaries or && chains.

JSX visual 2


Why key Matters in Lists

When you render a list with .map(), React requires a key prop on each element. This is not a suggestion — it directly affects how React updates the DOM.

The Analogy

Imagine a teacher with a class of 30 students. If every student wears a name tag (a unique key), the teacher can instantly identify who's new, who left, and who moved seats. Without name tags, the teacher has to compare faces one by one with a class photo — slower and error-prone.

React's reconciliation algorithm works the same way. Keys are name tags for list items.

How React Uses Keys

// Without key — React uses index by default (and warns you)
function TodoList({ todos }) {
  return (
    <ul>
      {todos.map((todo) => (
        <li key={todo.id}>   {/* unique, stable key */}
          {todo.text}
        </li>
      ))}
    </ul>
  );
}

When the list changes (item added, removed, or reordered), React compares old keys to new keys:

  • Same key, same position — keep the DOM node, update props if changed
  • Same key, different position — move the DOM node (not recreate)
  • New key — create a new DOM node
  • Missing key — remove the DOM node

Why Index as Key Is Dangerous

// DANGEROUS — using index as key
{todos.map((todo, index) => (
  <li key={index}>{todo.text}</li>
))}

Suppose you have three items: ["A", "B", "C"] with keys [0, 1, 2].

You delete "A". Now the array is ["B", "C"] with keys [0, 1].

React sees: key 0 had "A", now key 0 has "B" — it thinks key 0 changed content. Key 1 had "B", now has "C" — another content change. Key 2 is gone — remove it.

React updated two DOM nodes and removed one. The correct behavior (remove key "A", keep "B" and "C") would have touched one node.

Worse: if list items have local state (like input fields), the state sticks to the index, not the item. Deleting the first item shifts all state up by one — inputs show the wrong values.

What Makes a Good Key

// GOOD — unique, stable identifier from your data
<li key={user.id}>{user.name}</li>
<li key={product.sku}>{product.name}</li>
<li key={email}>{email}</li>

// OK — only if list is static and never reordered
<li key={index}>{staticItem}</li>

// BAD — random values create new keys every render
<li key={Math.random()}>{item}</li>       // new key each render = full remount
<li key={crypto.randomUUID()}>{item}</li> // same problem

Rules for keys:

  1. Unique among siblings — keys must be unique within the same list, not globally
  2. Stable — the same item must produce the same key across re-renders
  3. Not index — unless the list is static and never reordered or filtered
  4. Not random — random keys destroy and recreate every element on every render

JSX visual 3


Common Mistakes

1. Using class Instead of className

This is the most common mistake when switching from HTML to JSX. The code still runs — React even shows a warning in the console — but your CSS classes won't apply. Always use className.

2. The 0 && Rendering Bug

Writing {count && <Component />} when count can be 0 renders a literal 0 on screen. Always convert to a proper boolean: {count > 0 && <Component />} or use a ternary.

3. Using Random or Unstable Keys in Lists

Using Math.random() as a key forces React to destroy and recreate every list item on every re-render — destroying local state, killing performance, and breaking animations. Always use a stable identifier from your data.


Interview Questions

Q: What is JSX and what does it compile to?

Q: Why can't you use class and for attributes in JSX?

Because class and for are reserved keywords in JavaScript. Since JSX is JavaScript syntax, they would cause conflicts. React uses className and htmlFor instead. These map to the same DOM properties — element.className and label.htmlFor are the actual DOM API names anyway.

Q: What's the difference between {count && <Component />} and {count > 0 && <Component />}?

Q: Why does React need the key prop in lists? What happens if you use the array index as the key?

Q: Can you use if-else inside JSX? If not, what alternatives do you use?

You cannot use if-else directly inside JSX because JSX only accepts expressions, and if is a statement. The three alternatives are: ternary operator (condition ? A : B), logical AND (condition && element), and early return (handling conditions before the main return statement).


Quick Reference -- Cheat Sheet

JSX RULES AT A GLANCE
=====================

HTML Attribute     →  JSX Attribute
─────────────────────────────────────
class              →  className
for                →  htmlFor
onclick            →  onClick
tabindex           →  tabIndex
maxlength          →  maxLength
style="..."        →  style={{ ... }}
<img>              →  <img />
<br>               →  <br />

WHAT JSX COMPILES TO
====================
<Tag prop="val">child</Tag>

React.createElement(Tag, { prop: "val" }, "child")

CONDITIONAL RENDERING
=====================
if-else       →  ternary:     {cond ? <A/> : <B/>}
show/hide     →  logical AND: {cond && <A/>}
guard clause  →  early return: if (!x) return <Fallback/>

DANGER: {0 && <X/>} renders "0" — use {n > 0 && <X/>}

KEY RULES
=========
- Must be unique among siblings
- Must be stable across re-renders
- Use data IDs, not array index (unless static list)
- NEVER use Math.random() as key

Previous: Lesson 1.2 -- Virtual DOM & Reconciliation Next: Lesson 1.4 -- Component Types & Composition


This is Lesson 1.3 of the React Interview Prep Course -- 10 chapters, 42 lessons.

On this page