⚛️ Curriculum Track • Frontend Engineering & React

Unit 3: JSX & UI Rendering

Deep dive into JavaScript XML (JSX): syntactic compilation to React.createElement, fundamental rules of JSX, dynamic expression evaluation, multi-branch conditional rendering, rendering lists with stable keys, React Fragments, camelCase inline styling, CSS class composition, component rendering, and clean JSX commenting.

📚 Module: Unit 3 of 12 Core Units
⚛️ Core Syntax: JSX to React.createElement
🎨 Styling: Inline Styles & CSS Modules
📖 Format: 100% Pure Natural English (Zero LaTeX)
01

What is JSX & How It Works Under the Hood

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. Although JSX visually resembles HTML, it is not HTML and the browser cannot execute it natively.

📌 The Compilation Process: Babel and esbuild

Before running in the browser, build tools (such as Vite with esbuild or Babel) compile every JSX tag into standard JavaScript function calls: React.createElement() (or the modern jsx-runtime).

Side-by-Side Compilation Comparison

What You Author (JSX)
const element = (
  <h1 className="hero-title" id="main-heading">
    Hello, <span>World</span>!
  </h1>
);

Intuitive, readable, visually describes the intended UI structure.

What the Compiler Emits (Pure JS)
const element = React.createElement(
  "h1",
  { className: "hero-title", id: "main-heading" },
  "Hello, ",
  React.createElement("span", null, "World"),
  "!"
);

Returns a lightweight JavaScript object called a React Element.

Anatomy of a React Element Object

Calling React.createElement() produces a plain JavaScript object:

{
  type: "h1",
  props: {
    className: "hero-title",
    id: "main-heading",
    children: ["Hello, ", { type: "span", props: { children: "World" } }, "!"]
  },
  key: null,
  ref: null
}

React uses these element trees to construct and reconcile the Virtual DOM before committing minimal patches to the real browser DOM.

02

The Fundamental Rules of JSX

Because JSX compiles into JavaScript function calls and object expressions, it enforces three strict syntactic rules:

Rule 1: Return a Single Root Element

A component must return exactly one top-level root element. If you need to render multiple siblings, wrap them in a parent <div> or a React Fragment (<>...</>).

Rationale: A JavaScript function cannot return multiple values simultaneously without grouping them into an array or object.

Rule 2: Close All Tags Explicitly

In standard HTML, tags like <img>, <input>, and <br> can be left unclosed. In JSX, all self-closing tags must end with a forward slash:

<img src="avatar.jpg" alt="Profile" />
<input type="text" />
<br />
Rule 3: camelCase Property Names

Because JSX is closer to JavaScript than HTML, HTML attributes become camelCase JavaScript object keys:

  • class becomes className (since class is a reserved JS keyword).
  • for becomes htmlFor.
  • tabindex becomes tabIndex.
  • onclick becomes onClick.
03

Embedding Dynamic Expressions in JSX

JSX allows you to inject dynamic JavaScript variables, mathematical calculations, function calls, and property lookups directly into your markup using curly braces { ... }.

Dynamic Expressions in Action
function UserBadge() {
  const user = { firstName: "Sarah", lastName: "Connor", role: "Administrator" };
  const notificationCount = 5;

  const getGreeting = () => "Welcome back";

  return (
    <div className="badge-card">
      {/* 1. String variable interpolation */}
      <h3>{getGreeting()}, {user.firstName} {user.lastName}!</h3>

      {/* 2. Embedded JavaScript math */}
      <p>Active session duration: {12 * 5} minutes</p>

      {/* 3. Passing dynamic attribute props */}
      <span title={`Role: ${user.role}`}>
        Notifications: {notificationCount * 2}
      </span>
    </div>
  );
}
⚠️ Statement vs. Expression: What Can Go Inside Curly Braces?

You can only place JavaScript expressions (code that evaluates to a value) inside JSX curly braces. You cannot place JavaScript statements:

• Allowed (Expressions): {user.name}, {1 + 1}, {isActive ? "Yes" : "No"}, {formatDate(date)}.
• Forbidden (Statements): {if (x > 10) { return true; }}, {for (let i = 0; i < 5; i++) { ... }}. Statements do not return values and cause compilation syntax errors.

04

Conditional Rendering in React

React allows you to render completely different markup branches based on dynamic state or props. There are four primary patterns:

1. Ternary Operator (If / Else)

Ideal for choosing between two distinct UI elements:

function AuthButton({ isLoggedIn }) {
  return (
    <button>
      {isLoggedIn ? "Log Out" : "Sign In"}
    </button>
  );
}
2. Logical AND (&&) for Conditional Display

Ideal for rendering an element when true, and nothing when false:

function NotificationBanner({ hasUnread, count }) {
  return (
    <div>
      {/* Boolean coercion prevents the number 0 from rendering */}
      {hasUnread && count > 0 && (
        <span className="badge">{count} New Messages</span>
      )}
    </div>
  );
}
3. Early Returns (Component Level)

Return early from the function before rendering the main UI:

function ProductDetails({ product, isLoading, error }) {
  if (isLoading) return <div>Loading inventory...</div>;
  if (error) return <div>Failed to load product details.</div>;
  if (!product) return null; // Renders nothing!

  return <h1>{product.name} - {product.price} USD</h1>;
}
4. Variable Assignment Pattern

Pre-calculating elements into a variable before the return statement:

function Dashboard({ role }) {
  let adminControls = null;
  if (role === "admin") {
    adminControls = <button>Delete Server Database</button>;
  }

  return (
    <div>
      <h2>Workspace Dashboard</h2>
      {adminControls}
    </div>
  );
}
05

Rendering Lists & The Key Prop

To render dynamic collections of data (such as product catalogs, comment threads, or user directories), React utilizes JavaScript's native map() method.

How to Render Lists in JSX

List Rendering with map()
const techStack = [
  { id: "react", name: "React", category: "Frontend" },
  { id: "vite", name: "Vite", category: "Build Tool" },
  { id: "node", name: "Node.js", category: "Runtime" }
];

function TechList() {
  return (
    <ul>
      {techStack.map(tech => (
        <li key={tech.id}>
          <strong>{tech.name}</strong> — {tech.category}
        </li>
      ))}
    </ul>
  );
}

Why the Key Prop is Mandatory

Whenever you render a list of elements, React requires a unique key string or number on the outermost element returned by the map function.

The Role of Keys in Reconciliation

Keys give each list item a permanent, stable identity across re-renders. During reconciliation, React matches keys in the new Virtual DOM tree with keys in the old Virtual DOM tree:

  • If an item is prepended at the top, React reorders the existing real DOM nodes rather than destroying and recreating the entire list.
  • Preserves local input focus and component state inside list items.
Anti-Pattern: Using Array Index as Key

Using key={index} is an anti-pattern that leads to severe rendering glitches:

// DANGEROUS ANTI-PATTERN:
{items.map((item, index) => (
  <TodoItem key={index} text={item.text} />
))}

If items are sorted, filtered, or deleted, their array indices shift! React mistakenly associates the old item's internal state with the new item at that index. Always use unique database IDs (e.g., item.id).

06

React Fragments: Zero Overhead Wrapping

Because JSX components must return a single root element, developers historically wrapped siblings inside unnecessary wrapper <div> tags.

⚠️ The "Div Soup" Problem

Excessive wrapping <div> elements bloat the browser DOM tree, degrade rendering performance, and completely break CSS Flexbox, CSS Grid layouts, and HTML table structures (e.g., wrapping multiple <td> elements inside a <div> creates invalid HTML).

The Solution: React Fragments

A Fragment lets you group multiple children together without creating an extra DOM node in the browser:

Short Syntax (Empty Angle Brackets)

The standard shorthand used in 95% of components:

function PageHeader() {
  return (
    <>
      <h1>Main Title</h1>
      <p>Subheading text without a wrapper div!</p>
    </>
  );
}
Explicit Syntax (When Keys Are Needed)

Required when rendering a list of fragments that require a key:

import { Fragment } from "react";

function TermList({ terms }) {
  return (
    <dl>
      {terms.map(t => (
        <Fragment key={t.id}>
          <dt>{t.term}</dt>
          <dd>{t.description}</dd>
        </Fragment>
      ))}
    </dl>
  );
}
07

Styling in React: Inline Styles vs. CSS Classes

React provides multiple methodologies for styling user interface components:

1. Inline Styles in JSX

Unlike HTML where style="..." takes a plain string, React's style attribute accepts a JavaScript object. This requires double curly braces: the outer pair enters JavaScript expression mode, and the inner pair defines the object literal:

function Card({ isUrgent }) {
  return (
    <div style={{
      backgroundColor: isUrgent ? "#fee2e2" : "#ffffff",
      fontSize: "14px",          // camelCase property
      padding: "16px 20px",
      borderRadius: "8px",       // camelCase
      border: "1px solid #e2e8f0"
    }}>
      Critical System Notice
    </div>
  );
}
  • CSS property names must be written in camelCase (e.g., background-color becomes backgroundColor).
  • Values are strings or numbers (numbers like fontSize: 16 automatically append px).
2. CSS Classes via className

The standard industry approach for production codebases. You define classes in external CSS files or CSS Modules and attach them via className:

// App.jsx
import "./Button.css";

function ActionButton({ variant = "primary", isActive }) {
  // Composing classes dynamically
  const btnClass = `btn btn-${variant} ${isActive ? "active" : ""}`;

  return (
    <button className={btnClass}>
      Submit Form
    </button>
  );
}

Enables full CSS features: media queries, pseudo-classes (:hover, :focus), and keyframe animations that inline styles cannot handle.

08

Rendering Components & Composition

Components can be rendered by using their function name as a self-closing or opening/closing tag inside JSX:

Component Composition & Children Prop
// 1. Defining a reusable container component
function ModalContainer({ title, children }) {
  return (
    <div className="modal-backdrop">
      <div className="modal-dialog">
        <h2>{title}</h2>
        {/* The special 'children' prop renders nested content */}
        <div className="modal-body">{children}</div>
      </div>
    </div>
  );
}

// 2. Composing the Modal in an App component
export default function App() {
  return (
    <main>
      <ModalContainer title="User Authentication">
        <p>Please enter your single sign-on credentials below:</p>
        <input type="email" placeholder="user@company.com" />
        <button>Sign In</button>
      </ModalContainer>
    </main>
  );
}
How React Differentiates Native HTML from Custom Components

React uses the first letter of the tag name to determine how to compile it:

  • Lowercase tags (e.g., <div>, <button>, <span>): React treats these as built-in browser DOM elements and passes them as strings to React.createElement("div").
  • Uppercase tags (e.g., <UserProfile>, <Card>): React treats these as custom functional components and passes the direct function identifier to React.createElement(UserProfile).
09

Writing Comments in JSX

Because JSX sits at the intersection of JavaScript and HTML, writing comments requires a specific syntax:

Correct Comment Syntax in JSX
function CommentDemo() {
  // 1. Standard single-line JavaScript comment (outside JSX return)
  /* Multi-line JS comment outside JSX */

  return (
    <div>
      {/* 2. Standard single-line comment INSIDE JSX markup */}
      <h1>Main Dashboard</h1>

      {/*
        3. Multi-line comment inside JSX markup:
        Explaining complex UI layout structures
        across multiple paragraphs.
      */}
      <p>Content visible to users.</p>
    </div>
  );
}

To comment inside JSX markup, you must enter JavaScript expression mode via curly braces {/* ... */} and use standard C-style multi-line comment delimiters.

10

Complete Standalone Practical Implementation

Below is a complete, self-contained modern React component file demonstrating all concepts learned in this unit: JSX expressions, conditional branches, dynamic list mapping with unique keys, Fragments, inline styles, dynamic class names, and component composition.

src/ProjectDashboard.jsx • Production JSX Component Masterclass
import { Fragment, useState } from "react";

// Sub-component 1: Status indicator with inline styles
function PriorityPill({ priority }) {
  const isHigh = priority === "High";
  return (
    <span style={{
      padding: "2px 8px",
      borderRadius: "4px",
      fontSize: "11px",
      fontWeight: 700,
      backgroundColor: isHigh ? "#fee2e2" : "#e0f2fe",
      color: isHigh ? "#b91c1c" : "#0369a1"
    }}>
      {priority} Priority
    </span>
  );
}

// Sub-component 2: Reusable task item card
function TaskCard({ id, title, completed, priority, onToggle }) {
  return (
    <div style={{
      display: "flex",
      justifyContent: "space-between",
      alignItems: "center",
      padding: "12px 16px",
      borderBottom: "1px solid #f1f5f9"
    }}>
      <div style={{ display: "flex", alignItems: "center", gap: "10px" }}>
        <input
          type="checkbox"
          checked={completed}
          onChange={() => onToggle(id)}
        />
        <span style={{
          textDecoration: completed ? "line-through" : "none",
          color: completed ? "#94a3b8" : "#1e293b"
        }}>
          {title}
        </span>
      </div>
      <PriorityPill priority={priority} />
    </div>
  );
}

// Primary Root Component
export default function ProjectDashboard() {
  const [tasks, setTasks] = useState([
    { id: "task-01", title: "Configure Vite build pipeline", completed: true, priority: "High" },
    { id: "task-02", title: "Implement Virtual DOM unit tests", completed: false, priority: "High" },
    { id: "task-03", title: "Write component styling architecture", completed: false, priority: "Normal" }
  ]);

  const [showCompletedOnly, setShowCompletedOnly] = useState(false);

  const toggleTask = (taskId) => {
    setTasks(prev => prev.map(t =>
      t.id === taskId ? { ...t, completed: !t.completed } : t
    ));
  };

  // Filter tasks based on conditional filter toggle
  const visibleTasks = showCompletedOnly
    ? tasks.filter(t => t.completed)
    : tasks;

  const completedCount = tasks.filter(t => t.completed).length;

  return (
    <>
      <header style={{ marginBottom: "20px" }}>
        <h1 style={{ fontSize: "22px", color: "#0f172a" }}>
          Engineering Sprint Dashboard
        </h1>
        {/* Conditional rendering with ternary */}
        <p style={{ color: "#64748b", fontSize: "13px" }}>
          {completedCount === tasks.length
            ? "🎉 All sprint tasks completed successfully!"
            : `Completed ${completedCount} of ${tasks.length} total deliverables.`}
        </p>
      </header>

      <div style={{
        background: "#ffffff",
        border: "1px solid #e2e8f0",
        borderRadius: "10px",
        overflow: "hidden"
      }}>
        <div style={{ padding: "12px 16px", background: "#f8fafc", borderBottom: "1px solid #e2e8f0" }}>
          <label style={{ fontSize: "12.5px", color: "#334155", cursor: "pointer" }}>
            <input
              type="checkbox"
              checked={showCompletedOnly}
              onChange={() => setShowCompletedOnly(!showCompletedOnly)}
              style={{ marginRight: "8px" }}
            />
            Show Completed Tasks Only
          </label>
        </div>

        {/* Dynamic list rendering using map and unique keys */}
        {visibleTasks.length > 0 ? (
          visibleTasks.map(task => (
            <TaskCard
              key={task.id}
              id={task.id}
              title={task.title}
              completed={task.completed}
              priority={task.priority}
              onToggle={toggleTask}
            />
          ))
        ) : (
          <div style={{ padding: "20px", textAlign: "center", color: "#94a3b8" }}>
            No tasks match the active filter.
          </div>
        )}
      </div>
    </>
  );
}
11

Academic Viva Voce & Technical Interview Masterclass

Q1: What does JSX compile to under the hood, and can browsers run JSX natively?

Answer: Browsers cannot run JSX natively because JSX is not part of the standard ECMAScript specification. During the project build process, compilers like Babel or esbuild transform JSX tags into standard JavaScript function calls: React.createElement(type, props, ...children). Calling this function produces a lightweight plain JavaScript object known as a React Element, which describes the desired DOM node to the Virtual DOM engine.

Q2: Why must React components return a single root element, and how do Fragments solve this?

Answer: Under the hood, JSX compiles into a standard JavaScript function call (React.createElement). Since a JavaScript function cannot return multiple return values simultaneously without wrapping them into a single data structure, returning multiple sibling JSX tags without a parent container triggers a syntax error. React Fragments (<>...</> or <Fragment>...</Fragment>) solve this by allowing you to group multiple child elements together without injecting an unnecessary wrapper <div> into the actual physical browser DOM.

Q3: Why is using the array index as a "key" prop considered an anti-pattern in React?

Answer: The key prop provides a stable identity for React to match list items between renders during reconciliation. If you use array indices (key={index}) and items are reordered, inserted at the top, or filtered, their indices shift dynamically. React identifies elements purely by key and component type; if the keys shift, React mistakenly attaches previous local component state or DOM input values to the wrong data items, causing subtle visual and data-corruption bugs.

Q4: Why does React use "className" and "htmlFor" instead of standard HTML "class" and "for"?

Answer: Because JSX is a syntax extension of JavaScript rather than HTML, attribute names in JSX are compiled directly into property keys of a JavaScript object (the props object passed to React.createElement). In JavaScript, class is a reserved keyword used for declaring ES6 classes, and for is a reserved keyword for loops. To prevent syntactic ambiguity, React uses standard DOM API property naming: className and htmlFor.

Q5: Explain the dangerous edge case when using the logical AND (&&) operator with numerical values in JSX.

Answer: In JavaScript, the expression condition && <Component /> relies on short-circuit evaluation: if the left operand is truthy, it evaluates and returns the right operand. However, if the left operand is falsy, JavaScript returns the value of the left operand itself! If you write {items.length && <List />} and the array is empty, items.length evaluates to the number 0. Because 0 is a valid renderable value, React actually prints a stray literal 0 onto the browser screen! To prevent this, always ensure the left-hand side is a strict boolean: {items.length > 0 && <List />} or {Boolean(items.length) && ...}.

Q6: How do inline styles in React differ from standard HTML inline styles?

Answer: In HTML, inline styles are written as hyphenated strings (e.g., style="font-size: 14px; background-color: red;"). In React, the style attribute accepts a JavaScript object where CSS property names must be written in camelCase (e.g., style={{ fontSize: "14px", backgroundColor: "red" }}). Pixel values can be supplied as raw numbers, and dynamic variables can be injected directly without string concatenation.

12

Unit Summary & Key Conceptual Takeaways

Core Principles to Retain
  • JSX is Syntactic Sugar: Every JSX tag compiles down to React.createElement() function calls producing lightweight Virtual DOM element objects.
  • Three Core Rules: Return a single root element (or Fragment), self-close all void tags with a trailing slash (<img />), and write attributes in camelCase (className, htmlFor).
  • Expressions Inside Braces: Place JavaScript expressions inside { ... } to evaluate variables, calculations, and ternary branches; statements (like if or for) are forbidden inside JSX markup.
  • Stable Keys are Non-Negotiable: Always use persistent, unique database IDs for list keys; never use array indices on mutable lists.
  • Fragments Eliminate Div Soup: Use <>...</> to group siblings without bloating the browser DOM with redundant wrapper divs.
  • Double Curly Braces for Inline Styles: The outer pair enters JS expression mode; the inner pair creates the camelCase style object.
  • Capitalization Dictates Component Type: Lowercase tags represent native browser DOM nodes; PascalCase tags represent custom React components.