Skip to Content
Module 2: React Fundamentals2.3 JSX & Rendering Logic

Module 2.3: JSX & Rendering Logic

Banner showcasing JSX syntax and React logo.

🎯 Learning Objectives

By the end of this module, you’ll be able to:

  • Grasp what JSX is and how it compiles into JavaScript.
  • Differentiate between JSX and HTML (understanding className, htmlFor, and camelCase).
  • Leverage Fragments to group elements without adding extra nodes to the DOM.
  • Embed JavaScript expressions using the {} syntax.
  • Implement Conditional Rendering with &&, ternary operators, and early returns.
  • Render lists efficiently using .map() and understand the crucial role of the Key Prop.

1. What is JSX?

1.1. The Definition

JSX (JavaScript XML) is a syntax extension for JavaScript that lets you write HTML-like code directly within your JavaScript/TypeScript files.

// This is JSX const element = <h1>Hello, React!</h1>;

Think of JSX as more than just HTML or a template string; it’s essentially plain JavaScript after it’s been compiled.

1.2. How JSX Transforms

During the build process, tools like Babel or SWC transform your JSX into React.createElement() calls:

Diagram showing JSX transforming into React.createElement calls.

Before (JSX):

const element = ( <div className="container"> <h1>Hello</h1> <p>Welcome to React</p> </div> );

After Compilation (JavaScript):

const element = React.createElement( 'div', { className: 'container' }, React.createElement('h1', null, 'Hello'), React.createElement('p', null, 'Welcome to React') );

πŸ’‘ Heads Up: Starting with React 17, the new JSX transform means you don’t need to import React at the top of every file anymore. Build tools like Vite handle this configuration for you.

1.3. Why Embrace JSX?

AdvantageExplanation
IntuitiveWriting UI feels more like HTML, making it easier to read than raw createElement() calls.
Type-safeTypeScript provides robust checks for your props and elements.
ToolingEnjoy excellent IDE support with autocompletion and syntax highlighting.
Compile-timeCatch errors early in the development cycle, during the build process.

2. JSX Isn’t Quite HTML

2.1. Key Differences to Note

Illustration comparing HTML and JSX syntax side-by-side.

2.2. A Quick Comparison

HTML:

<label for="email" class="form-label">Email</label> <input type="email" id="email" class="form-input" tabindex="1" onclick="handleClick()" style="color: red;" />

JSX:

<label htmlFor="email" className="form-label">Email</label> <input type="email" id="email" className="form-input" tabIndex={1} onClick={handleClick} style={{ color: 'red' }} />

2.3. Self-Closing Tags

In JSX, all tags must be properly closed:

// ❌ HTML style - This will cause errors in JSX <img src="logo.png" /> <input type="text" /> <br /> // βœ… JSX - You must self-close them <img src="logo.png" /> <input type="text" /> <br />

2.4. Boolean Attributes

// HTML: The 'disabled' attribute doesn't need a value <button disabled>Submit</button> // JSX: You can write it like HTML or be explicit <button disabled>Submit</button> <button disabled={true}>Submit</button> // Conditionally disable a button <button disabled={isLoading}>Submit</button>

3. Fragments

3.1. The Problem: JSX Must Return a Single Element

JSX requires you to return one root element:

// ❌ Error: Adjacent JSX elements must be wrapped function App() { return ( <h1>Title</h1> <p>Content</p> ); } // βœ… Wrap them in a single element function App() { return ( <div> <h1>Title</h1> <p>Content</p> </div> ); }

The catch: Adding an unnecessary <div> can bloat your DOM, potentially interfering with CSS layouts (like flexbox or grid).

3.2. The Solution: Fragments

Fragments allow you to group elements together without adding an extra node to the DOM:

// Method 1: The Fragment component import { Fragment } from 'react'; // Import Fragment function App() { return ( <Fragment> <h1>Title</h1> <p>Content</p> </Fragment> ); } // Method 2: Short syntax (highly recommended) function App() { return ( <> <h1>Title</h1> <p>Content</p> </> ); }

3.3. Fragments with Keys

When rendering lists within a Fragment, you’ll need to use the <Fragment> component and provide a key:

import { Fragment } from 'react'; function Glossary({ items }) { return ( <dl> {items.map((item) => ( <Fragment key={item.id}> <dt>{item.term}</dt> <dd>{item.description}</dd> </Fragment> ))} </dl> ); }

⚠️ The short syntax <>...</> does not support key. You must use <Fragment key={...}> in this specific scenario.

4. Embedding Expressions

4.1. The {} Syntax

Use curly braces {} to embed JavaScript expressions directly into your JSX:

const name = 'React'; const version = 18; function App() { return ( <div> <h1>Hello, {name}!</h1> <p>Version: {version}</p> <p>Sum: {1 + 1}</p> <p>Uppercase: {name.toUpperCase()}</p> <p>Current year: {new Date().getFullYear()}</p> </div> ); }

4.2. Expressions vs. Statements

You can use these (Expressions - they return a value):

{name} // Variables {1 + 1} // Math operations {isActive ? 'Yes' : 'No'} // Ternary operators {items.map(...)} // Method calls on arrays {formatDate(date)} // Function calls

You cannot use these (Statements - they don’t return a value):

// ❌ Errors - Statements are not allowed if (condition) { ... } for (let i = 0; ...) { ... } const x = 5

4.3. Inline Styles

Styles in JSX are represented as JavaScript objects, not strings:

// ❌ Incorrect - HTML style string <div style="background-color: red; font-size: 16px"> // βœ… Correct - JSX style (object) <div style={{ backgroundColor: 'red', fontSize: '16px' }}> // βœ… Separated into its own object for clarity const styles = { backgroundColor: 'red', fontSize: '16px', padding: '1rem', }; <div style={styles}>Content</div>

πŸ’‘ Pro-Tip: CSS properties in JSX styles use camelCase (e.g., backgroundColor instead of background-color).

5. Conditional Rendering

Illustration of different conditional rendering patterns in React.

5.1. The && (AND) Operator

Render an element only if the condition is true:

function Notification({ count }) { return ( <div> <h1>Messages</h1> {count > 0 && <p>You have {count} unread messages.</p>} </div> ); } // If count = 5 β†’ Displays: "You have 5 unread messages." // If count = 0 β†’ Nothing is displayed

⚠️ Watch out for zero: Using {count && <p>...</p>} will render the number 0 if count is 0. Use {count > 0 && ...} to prevent this.

5.2. The Ternary Operator

Render one of two options based on a condition:

function LoginButton({ isLoggedIn }) { return <button>{isLoggedIn ? 'Logout' : 'Login'}</button>; } // If isLoggedIn = true β†’ Displays: "Logout" // If isLoggedIn = false β†’ Displays: "Login"

Render different components:

function Dashboard({ user }) { return <div>{user ? <UserProfile user={user} /> : <LoginForm />}</div>; }

5.3. Early Return

Stop the rendering process early if a condition isn’t met:

function UserProfile({ user }) { // Early return if no user is provided if (!user) { return <p>Please log in.</p>; } // This part only runs if a user object exists return ( <div> <h1>{user.name}</h1> <p>{user.email}</p> </div> ); }

5.4. When to Use What?

PatternBest For
&&Show or hide an element.
? :Choose between two distinct options.
Early returnComplex logic with multiple conditions.

6. Rendering Lists

6.1. Harnessing .map()

Use the Array.map() method to transform an array of data into a list of elements:

const fruits = ['Apple', 'Banana', 'Orange']; function FruitList() { return ( <ul> {fruits.map((fruit) => ( <li>{fruit}</li> ))} </ul> ); }

6.2. The key Prop - A Must-Have

Every item in a list must have a unique key prop:

const fruits = ['Apple', 'Banana', 'Orange']; function FruitList() { return ( <ul> {fruits.map((fruit, index) => ( <li key={fruit}>{fruit}</li> // Using the fruit name as a key here ))} </ul> ); }

Illustration of how React uses keys to efficiently update lists.

6.3. Why Do We Need Keys?

Keys are essential for React to identify which elements have changed, been added, or removed in a list:

// Without a key - React doesn't know which element is which // β†’ It might re-render the entire list (slow) // With a key - React tracks each element by its key // β†’ It only updates the elements that have actually changed (fast)

6.4. Why Using Index as a Key is Bad Practice

// ❌ Bad practice - Using the index as a key { items.map((item, index) => <Item key={index} data={item} />); }

The Problem: If you add, delete, or reorder items in your list, the index will change. This misleads React into thinking it’s a completely different item:

BeforeAfter (adding item at the beginning)
key=0: Applekey=0: Mango (incorrect!)
key=1: Bananakey=1: Apple (incorrect!)
key=2: Orangekey=2: Banana (incorrect!)
key=3: Orange

The Consequences:

  • State can get mixed up between items.
  • Animations and transitions might not work as expected.
  • Performance can suffer due to unnecessary re-renders.

6.5. The Right Way to Use Keys

// βœ… Good - Use a unique ID from your data const users = [ { id: 'u1', name: 'Alice' }, { id: 'u2', name: 'Bob' }, ]; function UserList() { return ( <ul> {users.map((user) => ( <li key={user.id}>{user.name}</li> ))} </ul> ); }

Valid keys include:

  • Database IDs (user.id, product.sku)
  • Unique business identifiers (email, uuid)
  • Generated IDs (if your data lacks inherent unique identifiers)

6.6. Rendering Lists of Objects

const products = [ { id: 1, name: 'Laptop', price: 999 }, { id: 2, name: 'Phone', price: 699 }, ]; function ProductList() { return ( <div className="product-grid"> {products.map((product) => ( <div key={product.id} className="product-card"> <h3>{product.name}</h3> <p>${product.price}</p> </div> ))} </div> ); }

7. Further Reading

Last updated on