JSX Deep Dive: Lists, Conditional Rendering, Events & Props

JSX Deep Dive: Lists, Conditional Rendering, Events & Props

Introduction

In the previous session, you set up your first React project with Vite, learned JSX basics, and created simple components. This session goes deeper — covering the patterns you will use every day when writing React.

This session is split into two parts. Part 1 covers the JSX-HTML differences, rendering lists from data, Fragments, inline styles, and component organization. Part 2 covers conditional rendering (showing and hiding content), event handling, and a first look at props.

What You’ll Learn

Part 1

  • All the differences between JSX and HTML
  • How to render lists from arrays using .map() and how it compares to vanilla JS
  • Why React needs the key prop and how to use it correctly
  • When to use Fragments and why extra <div> wrappers cause real problems
  • When and how to use inline styles vs CSS files
  • How to organize components into files and folders with proper imports/exports

Part 2

  • Three patterns for conditional rendering: &&, ternary (? :), and early return
  • How to render nothing with null
  • How to apply conditional CSS classes
  • Event handling — responding to clicks and input with onClick and onChange
  • Props preview — passing data to components to make them reusable

JSX vs HTML — Complete Reference

JSX looks like HTML, but it is actually JavaScript. Because JSX is compiled into JavaScript function calls, it must follow JavaScript’s rules — not HTML’s rules. Words like class and for are reserved keywords in JavaScript, so JSX uses alternatives.

HTMLJSXReason
class="card"className="card"class is a reserved word in JavaScript
for="email"htmlFor="email"for is a reserved word in JavaScript
<br><br />All tags must be explicitly closed
<img src="..."><img src="..." />Self-closing tags need the /
<input type="text"><input type="text" />Same — must self-close
style="color: red"style={{ color: 'red' }}Styles are JavaScript objects
onclick="..."onClick={...}Event handlers use camelCase
tabindex="0"tabIndex={0}Attributes use camelCase
The pattern: If an HTML attribute is a reserved word in JavaScript (class, for), JSX uses an alternative name. If an attribute is multi-word (onclick, tabindex), JSX uses camelCase (onClick, tabIndex). Once you see the pattern, it becomes second nature.

Rendering Lists with .map()

Almost every web application displays lists of data: a feed of posts, search results, a shopping cart, a list of contacts. In vanilla JavaScript, you built these lists by creating HTML strings and injecting them with innerHTML. React takes a different approach: transform arrays of data into arrays of JSX elements using .map(), and let React handle all the DOM updates.

Vanilla JS vs React — Side by Side

You already know .map() from Weeks 5 and 6. In React, the concept is identical — only the output format changes.

// Vanilla JS — build a string, inject into DOM
const users = ["Alice", "Bob", "Carol"];
const html = users.map(user => `<li>${user}</li>`).join("");
document.querySelector("#list").innerHTML = html;

You build an HTML string with template literals, join the array, and set innerHTML. You are responsible for finding the DOM element and updating it yourself.

// React — return JSX directly, React handles the DOM
function UserList() {
const users = ["Alice", "Bob", "Carol"];

return (
  <ul>
    {users.map((user) => (
      <li key={user}>{user}</li>
    ))}
  </ul>
);
}

.map() returns JSX elements instead of strings. No .join(""), no innerHTML, no document.querySelector. React renders them into the DOM for you.

Rendering Arrays of Objects

Most real data comes as arrays of objects — from APIs, databases, or local state. Map over them the same way:

function TeamList() {
  const team = [
    { id: 1, name: "Alice", role: "Developer" },
    { id: 2, name: "Bob", role: "Designer" },
    { id: 3, name: "Carol", role: "Project Manager" },
  ];

  return (
    <div>
      <h2>Our Team</h2>
      {team.map((member) => (
        <div key={member.id} className="team-member">
          <h3>{member.name}</h3>
          <p>{member.role}</p>
        </div>
      ))}
    </div>
  );
}
Notice the key prop on each mapped element. React requires a unique key for every item in a list. See the next section for why.

Destructuring Inside .map()

When your objects have many properties, destructure them directly in the .map() callback to keep the JSX clean:

{team.map(({ id, name, role }) => (
  <div key={id} className="team-member">
    <h3>{name}</h3>
    <p>{role}</p>
  </div>
))}

The key Prop

When you render a list with .map(), React needs a way to identify each item so it can efficiently update the DOM when the list changes. Without keys, React cannot tell which items were added, removed, or moved — it has to destroy and rebuild the entire list on every change.

Think of it like a classroom roster. If the teacher only knows students by their seat number (index) and two students swap seats, the teacher thinks they are different people. If the teacher knows students by their student ID, swapping seats causes no confusion. Keys are student IDs for your list items.

Rules for Keys

// GOOD — unique, stable identifier from your data
{users.map((user) => (
  <li key={user.id}>{user.name}</li>
))}

// OK — unique string value (when items don't have IDs)
{fruits.map((fruit) => (
  <li key={fruit}>{fruit}</li>
))}

// BAD — array index as key (shifts when items change)
{users.map((user, index) => (
  <li key={index}>{user.name}</li>
))}

What Happens Without Stable Keys

Imagine you have a list of three items and you remove the middle one:

With stable keys (using IDs):

Before:   [Alice (key=1)] [Bob (key=2)] [Carol (key=3)]
After:    [Alice (key=1)]               [Carol (key=3)]

React sees: key=2 is gone → remove that one element. Done.

With index keys:

Before:   [Alice (key=0)] [Bob (key=1)] [Carol (key=2)]
After:    [Alice (key=0)] [Carol (key=1)]

React sees: key=0 same. key=1 changed from Bob to Carol → update it.
            key=2 is gone → remove it.
React updated TWO elements instead of ONE.

With index keys, React does more work than necessary, and in complex cases (like form inputs inside list items) it causes visible bugs where the wrong data appears in the wrong row.

Key Rules Summary

RuleExample
Keys must be unique among siblingsNo two items in the same list should share a key
Keys should be stableUse IDs from your data, not random values
Keys go on the outermost element in the mapThe first JSX element inside .map()
Keys are not passed as a propYou cannot access props.key inside the component
If you forget keys, React will show a console warning: “Each child in a list should have a unique ‘key’ prop.” Always check your browser console for this warning when working with lists.

Fragments

React components must return a single root element. Without Fragments, you would be forced to wrap everything in a <div>, even when that extra <div> causes real problems.

Fragments let you group multiple elements together without adding any extra nodes to the DOM. They are invisible wrappers that satisfy React’s single-root requirement while keeping your HTML clean.

// Extra div (sometimes unwanted)
return (
  <div>
    <h1>Title</h1>
    <p>Content</p>
  </div>
);

// Fragment — no extra DOM node
return (
  <>
    <h1>Title</h1>
    <p>Content</p>
  </>
);

When Extra <div> Wrappers Break Your Layout

Extra <div> elements are not just clutter — they can break CSS layouts. This is especially common with Flexbox and Grid, where the parent-child relationship between elements matters.

// Parent uses Flexbox — expects direct children
function Navigation() {
return (
  <nav style={{ display: 'flex', gap: '16px' }}>
    <Logo />
    <NavLinks />
    <UserMenu />
  </nav>
);
}

// NavLinks wraps items in a div — breaks the flex layout!
function NavLinks() {
return (
  <div>  {/* This div becomes a single flex child */}
    <a href="/home">Home</a>
    <a href="/about">About</a>
    <a href="/contact">Contact</a>
  </div>
);
}

The extra <div> becomes a single flex child, so the three links are no longer direct flex children of the <nav>.

// Fragment — links become direct flex children
function NavLinks() {
return (
  <>
    <a href="/home">Home</a>
    <a href="/about">About</a>
    <a href="/contact">Contact</a>
  </>
);
}

With a Fragment, the <a> elements are injected directly into the <nav> as flex children. The layout works as intended.

Fragments with Keys

If you need a key on a Fragment (inside .map()), use the explicit Fragment import. The short syntax <>...</> does not support the key prop.

import { Fragment } from 'react';

{items.map((item) => (
  <Fragment key={item.id}>
    <dt>{item.term}</dt>
    <dd>{item.definition}</dd>
  </Fragment>
))}

When to use which syntax:

  • <>...</> — most of the time, when you just need to group elements
  • <Fragment key={...}> — only when you need a key (inside .map())

Inline Styles in JSX

In HTML, the style attribute is a string: style="color: red; font-size: 18px". In JSX, everything inside {} is JavaScript, so the style attribute takes a JavaScript object instead. CSS property names must be written in camelCase, and values are usually strings.

Most of the time, use CSS files for styling. Inline styles are best reserved for dynamic values — styles that change based on data.

// HTML
<div style="background-color: #f0f0f0; padding: 16px;">

// JSX
<div style={{ backgroundColor: '#f0f0f0', padding: '16px' }}>

Why Double Curly Braces?

  • The outer {} is the JSX expression delimiter
  • The inner {} is a JavaScript object literal

Style Property Names

CSS properties become camelCase in JSX:

CSSJSX Style Object
background-colorbackgroundColor
font-sizefontSize
border-radiusborderRadius
margin-topmarginTop
z-indexzIndex

Dynamic Styles — When Inline Styles Shine

Inline styles are most useful when the style values depend on data:

function ProgressBar({ percentage }) {
  return (
    <div style={{ width: '100%', backgroundColor: '#e5e7eb', borderRadius: '4px' }}>
      <div style={{
        width: `${percentage}%`,
        backgroundColor: percentage > 75 ? 'green' : percentage > 50 ? 'yellow' : 'red',
        height: '20px',
        borderRadius: '4px',
        transition: 'width 0.3s ease',
      }}>
        {percentage}%
      </div>
    </div>
  );
}

This would be difficult with a CSS file alone because width and backgroundColor are computed from the percentage prop.

When to use inline styles vs CSS files: Use CSS files for most styling. Inline styles are useful for dynamic values that change based on data (like a progress bar width or a user-chosen color).

Component Organization

As your project grows, you need a clear structure for your components.

One Component Per File

Each component gets its own .jsx file, named to match the component:

src/
├── components/
│   ├── Header.jsx       → exports Header
│   ├── Footer.jsx       → exports Footer
│   ├── ProfileCard.jsx  → exports ProfileCard
│   └── Button.jsx       → exports Button
├── App.jsx
├── main.jsx
└── index.css

Component + CSS Together

As your project grows, group each component with its CSS:

        • Card.jsx
        • Card.css
        • Button.jsx
        • Button.css
        • Header.jsx
        • Header.css
    • App.jsx
    • App.css
    • main.jsx
    • index.css
  • This pattern is called co-location — keeping related files together.

    When to Create a New Component

    1. Is it reused? The same UI appears in multiple places → extract it
    2. Is it complex? The JSX is longer than ~30 lines → extract it
    3. Does it have a clear job? “This is the navigation bar” → extract it

    If the answer to any of these is yes, create a new component.

    The Full Import/Export Flow

    // src/components/Header.jsx — define and export the component
    import './Header.css';
    
    function Header() {
      return (
        <header className="header">
          <h1>My App</h1>
          <nav>
            <a href="/">Home</a>
            <a href="/about">About</a>
          </nav>
        </header>
      );
    }
    
    export default Header;
    // src/App.jsx — import and use the component
    import Header from './components/Header';
    
    function App() {
      return (
        <div>
          <Header />
          <main>
            <p>Welcome to the app!</p>
          </main>
        </div>
      );
    }
    
    export default App;

    Exports: Default vs Named

    // Button.jsx — one component per file (most common)
    function Button() {
    return <button className="btn">Click</button>;
    }
    export default Button;
    
    // Importing (no curly braces for default exports):
    import Button from './Button';

    Use default exports for components. Each file exports one thing.

    // Buttons.jsx — multiple related components
    export function PrimaryButton() {
    return <button className="btn btn-primary">Primary</button>;
    }
    export function SecondaryButton() {
    return <button className="btn btn-secondary">Secondary</button>;
    }
    
    // Importing (must use exact names with curly braces):
    import { PrimaryButton, SecondaryButton } from './Buttons';

    Use named exports for multiple small, related items in one file.


    Conditional Rendering

    React doesn’t have special template syntax for conditions (like v-if in Vue). Instead, you use regular JavaScript. The key shift is from imperative (Vanilla JS) to declarative (React):

    // Vanilla JS approach (Week 6) — imperative
    if (isLoggedIn) {
      welcomeMessage.classList.remove("hidden");
    } else {
      welcomeMessage.classList.add("hidden");
    }
    // React approach — declarative
    function Header({ isLoggedIn }) {
      return (
        <header>
          {isLoggedIn ? (
            <p className="welcome">Welcome back!</p>
          ) : (
            <button>Log In</button>
          )}
        </header>
      );
    }

    No classList.add, no classList.remove, no tracking hidden states.

    Pattern 1: && Operator (Show or Hide)

    Use && when you want to show something or show nothing:

    function Dashboard({ messageCount }) {
      return (
        <div>
          <h1>Dashboard</h1>
          {messageCount > 0 && (
            <p className="notification">
              You have {messageCount} new messages!
            </p>
          )}
        </div>
      );
    }

    How it works: If the left side is truthy, React renders the right side. If falsy, nothing renders.

    Watch out for zero! {count && <p>Messages</p>} will render the number 0 on screen when count is 0, because 0 is falsy but still a renderable value in React. Always use an explicit comparison: {count > 0 && <p>Messages</p>}.

    Pattern 2: Ternary Operator (Either/Or)

    Use the ternary when you want to choose between two things:

    function LoginButton({ isLoggedIn }) {
      return (
        <div>
          {isLoggedIn ? (
            <button>Log Out</button>
          ) : (
            <button>Log In</button>
          )}
        </div>
      );
    }

    How it works: condition ? (show this) : (show that) — the ? means “then” and : means “else.”

    Pattern 3: Early Return (Edge Cases)

    Use early return when you need to handle edge cases before the main UI:

    function UserProfile({ user }) {
      if (!user) {
        return <p>Loading user data...</p>;
      }
    
      if (user.error) {
        return <p className="error">Could not load profile.</p>;
      }
    
      return (
        <div className="profile">
          <h2>{user.name}</h2>
          <p>{user.email}</p>
          <p>{user.bio}</p>
        </div>
      );
    }

    How it works: Check for edge cases at the top of the component. If one matches, return immediately. The main UI at the bottom only runs when everything is normal.

    The order of early returns matters. Check the most critical conditions first (loading, errors) and work down to less critical ones (empty data). This prevents runtime errors like “Cannot read properties of undefined.”

    Rendering Nothing with null

    Sometimes a component should explicitly render nothing. Returning null tells React to skip this component entirely:

    function MaybeAlert({ message }) {
      if (!message) {
        return null; // Explicitly render nothing
      }
    
      return <div className="alert">{message}</div>;
    }
    When to use null vs &&: Use return null when the entire component should render nothing. Use && when you want to show or hide one piece within a larger component.

    When to Use Each Pattern

    PatternUse WhenReal Example
    &&Show or hide one thingBadge, notification, tooltip, “New!” label
    ? :Choose between two optionsLogin/logout button, light/dark theme, expand/collapse
    Early returnHandle edge cases before normal UILoading state, error state, empty data
    return nullThe entire component should render nothingConditional alert, optional tooltip

    Decision Flowchart

    Need to conditionally render content?
    │
    ├── Show or hide ONE thing?
    │   └── Use && operator
    │       Example: {isAdmin && <DeleteButton />}
    │
    ├── Choose between TWO things?
    │   └── Use ternary ? :
    │       Example: {isLoggedIn ? <Logout /> : <Login />}
    │
    ├── Handle edge cases before main UI?
    │   └── Use early return
    │       Example: if (isLoading) return <Spinner />;
    │
    └── Entire component should render nothing?
        └── return null
            Example: if (!message) return null;

    Conditional CSS Classes

    You often need to change an element’s styling based on a condition. Do this by conditionally setting the className prop.

    // Toggle between active and inactive styles
    <button className={`btn ${isActive ? "btn-active" : "btn-inactive"}`}>
      Click Me
    </button>
    
    // Add an optional class (empty string when false)
    <div className={`card ${isHighlighted ? "card-highlighted" : ""}`}>
      Card Content
    </div>
    
    // Dynamic class based on data value
    function StatusBadge({ status }) {
      return (
        <span className={`badge badge-${status}`}>
          {status}
        </span>
      );
    }
    Avoid using && directly in className. Writing className={\card ${isHighlighted && “card-highlighted”}`}will insertfalseinto the class string when the condition is falsy, producingclass=“card false”`. Always use a ternary with an empty string as the fallback.

    Event Handling in React

    Events vs Vanilla JS

    You already know DOM events from Week 5-6. In React, events are props you pass directly to JSX elements — no querySelector, no addEventListener:

    Vanilla JSReact JSX
    element.addEventListener('click', handler)onClick={handler}
    element.addEventListener('input', handler)onChange={handler}
    element.addEventListener('submit', handler)onSubmit={handler}

    Rule: All React event names are camelCase (onClick, onChange, onSubmit).

    The Handler Pattern

    Define the handler above the return, then pass it as a prop:

    function LikeButton() {
      function handleClick() {
        console.log("Liked!");
      }
    
      return <button onClick={handleClick}>Like </button>;
    }

    Naming convention: handle + what it handles: handleClick, handleSubmit, handleChange.

    The #1 Mistake — Parentheses

    This is the most common beginner error:

    // WRONG — calls the function immediately when the component renders!
    <button onClick={handleClick()}>Like</button>
    
    // RIGHT — passes the function to be called when clicked
    <button onClick={handleClick}>Like</button>

    Think of it as: handing someone your phone number vs calling them right now.

    Inline Arrow Functions

    // Inline arrow — fine for short logic
    <button onClick={() => console.log("clicked!")}>Click me</button>
    
    // Use an arrow function when passing arguments
    <button onClick={() => handleDelete(item.id)}>Delete</button>
    
    // Named handler — better for complex logic
    function handleDelete() {
      console.log("Deleting...");
      // more logic here
    }
    <button onClick={handleDelete}>Delete</button>

    The Event Object

    React passes an event object to every handler — same as vanilla JS:

    function SearchInput() {
      function handleChange(event) {
        console.log(event.target.value); // logs what the user typed
      }
    
      return <input onChange={handleChange} placeholder="Search..." />;
    }

    Common uses:

    • event.target.value → the input’s current text
    • event.preventDefault() → stop a form from refreshing the page
    • event.target → the element that fired the event
    function ContactForm() {
      function handleSubmit(event) {
        event.preventDefault(); // stop page reload
        console.log("Form submitted!");
      }
    
      return (
        <form onSubmit={handleSubmit}>
          <input type="text" placeholder="Your name" />
          <button type="submit">Submit</button>
        </form>
      );
    }
    Note: Right now, events let you see user actions in the console. To make the UI actually update in response (e.g., show what the user typed), you need useState — coming in Week 11. Getting familiar with event syntax now is essential preparation.

    Props Preview

    The Problem with Hardcoded Components

    Every component you have built so far shows the same data:

    function ProfileCard() {
      return (
        <div>
          <h2>Alice</h2>
          <p>Developer</p>
        </div>
      );
    }

    This component always shows Alice. To show Bob, you would need to copy-paste the whole component. That does not scale — imagine 50 profile cards.

    Props — Function Arguments for Components

    Props let you pass different data to the same component:

    // Usage — pass data like HTML attributes
    <ProfileCard name="Alice" role="Developer" />
    <ProfileCard name="Bob" role="Designer" />
    <ProfileCard name="Carol" role="PM" />
    
    // Definition — receive data via destructuring
    function ProfileCard({ name, role }) {
      return (
        <div>
          <h2>{name}</h2>
          <p>{role}</p>
        </div>
      );
    }

    One component definition → unlimited reuse with different data.

    Props with .map()

    The full pattern for data-driven UIs combines .map() with props:

    function TeamList() {
      const team = [
        { id: 1, name: "Alice", role: "Developer" },
        { id: 2, name: "Bob", role: "Designer" },
        { id: 3, name: "Carol", role: "PM" },
      ];
    
      return (
        <div>
          {team.map((member) => (
            <ProfileCard
              key={member.id}
              name={member.name}
              role={member.role}
            />
          ))}
        </div>
      );
    }

    This is the complete data-driven pattern: an array of objects + .map() + props = a list of components each showing different data.

    Week 10 Deep Dive: Props are a major topic. This is just a preview. In Week 10 (after Spring Break), you will cover: default prop values, prop validation with PropTypes, the children prop, passing functions as props, and common patterns for component communication.

    Common Patterns

    Rendering a Filtered List

    Combine .filter() and .map() to show only matching items:

    function ActiveUsers() {
      const users = [
        { id: 1, name: "Alice", active: true },
        { id: 2, name: "Bob", active: false },
        { id: 3, name: "Carol", active: true },
      ];
    
      return (
        <ul>
          {users
            .filter((user) => user.active)
            .map((user) => (
              <li key={user.id}>{user.name}</li>
            ))}
        </ul>
      );
    }

    Empty State Pattern

    Always handle the case when there is no data to show:

    function SearchResults({ results }) {
      if (results.length === 0) {
        return <p className="empty">No results found. Try a different search.</p>;
      }
    
      return (
        <div>
          {results.map((result) => (
            <div key={result.id}>
              <h3>{result.title}</h3>
              <p>{result.description}</p>
            </div>
          ))}
        </div>
      );
    }

    Putting It All Together

    Here is a complete component that uses every pattern from this session:

    function ProductCard({ product }) {
      // Early return for missing data
      if (!product) {
        return <p>Product not found</p>;
      }
    
      return (
        <div className="product-card">
          <h3>{product.name}</h3>
          <p>${product.price}</p>
    
          {/* && for optional badge */}
          {product.onSale && <span className="sale-badge">SALE</span>}
    
          {/* Ternary for stock status */}
          {product.inStock ? (
            <button>Add to Cart</button>
          ) : (
            <p className="out-of-stock">Out of Stock</p>
          )}
    
          {/* Ingredients list with .map() */}
          <ul>
            {product.tags.map((tag) => (
              <li key={tag}>{tag}</li>
            ))}
          </ul>
        </div>
      );
    }

    Troubleshooting & Common Mistakes

    I see ‘Each child in a list should have a unique key prop’

    Every element returned by .map() needs a key prop. Use a unique identifier from your data:

    // Missing key
    {items.map(item => <li>{item.name}</li>)}
    
    // With key
    {items.map(item => <li key={item.id}>{item.name}</li>)}

    If your data does not have IDs, use a unique string value. Avoid using array index as a key.

    My && condition renders ‘0’ on the screen

    When the left side of && is 0 (not false), React renders the 0. Use an explicit comparison:

    // Renders "0" when count is 0
    {count && <p>{count} messages</p>}
    
    // Renders nothing when count is 0
    {count > 0 && <p>{count} messages</p>}
    My inline styles aren’t working

    JSX styles use camelCase and are JavaScript objects, not strings.

    // Wrong — string style like HTML
    <div style="background-color: red">
    
    // Wrong — single curly braces (syntax error)
    <div style={ backgroundColor: 'red' }>
    
    // Correct — double curly braces, camelCase
    <div style={{ backgroundColor: 'red' }}>
    My click handler fires when the page loads, not when I click

    You accidentally called the function with (). Remove the parentheses:

    // Problem — handleClick() is called immediately on render
    <button onClick={handleClick()}>Click</button>
    
    // Fix — pass the reference, no parentheses
    <button onClick={handleClick}>Click</button>

    If you need to pass an argument, use an arrow function wrapper:

    <button onClick={() => handleDelete(item.id)}>Delete</button>
    My component doesn’t re-render when I change data

    If you are changing a variable inside the component and expecting the UI to update, that will not work yet. You need state (coming in Week 11). For now, components render once with their initial data.

    // This won't update the UI
    function Counter() {
      let count = 0;
      // Changing count here doesn't trigger a re-render
      return <p>{count}</p>;
    }
    
    // We'll learn useState in Week 11 to solve this!
    I get ‘Cannot find module’ when importing a component

    Check these things:

    1. The file path is correct (case-sensitive on Mac/Linux)
    2. The file extension is included if needed: import Card from './Card' or './Card.jsx'
    3. The component file has export default ComponentName at the bottom
    4. You are importing from the right directory (e.g., './components/Card')
    My conditional className includes ‘false’ in the output

    This happens when you use && inside a template literal for class names. Use a ternary with an empty string fallback instead:

    // Problem: class="card false"
    <div className={`card ${isHighlighted && "card-highlighted"}`}>
    
    // Solution: class="card " (harmless extra space)
    <div className={`card ${isHighlighted ? "card-highlighted" : ""}`}>
    My onChange isn’t logging anything

    Make sure you are passing the handler correctly (no ()) and the event parameter is named in the function:

    // Problem — missing event parameter
    function handleChange() {
      console.log(event.target.value); // ReferenceError!
    }
    
    // Fix — accept event as a parameter
    function handleChange(event) {
      console.log(event.target.value); // Works correctly
    }

    Key Takeaways

    🎯
    • JSX differences: className not class, close all tags, camelCase attributes, styles are objects
    • Render lists with .map() — always provide a unique key prop (use IDs from your data, not array index)
    • Keys help React track which items changed — think of them as student IDs for your list items
    • Fragments (<>...</>) wrap elements without adding extra DOM nodes — use them to avoid breaking Flexbox/Grid layouts
    • Inline styles use double curly braces and camelCase property names — best for dynamic values
    • Organize components: one per file, PascalCase names, group with CSS in folders, use export default
    • Three conditional patterns: && to show/hide one thing, ? : to choose between two things, early return for edge cases
    • return null renders nothing from the entire component; && skips one element within a component
    • Conditional classes: use ternary inside template literals, never && in className
    • Event handling: onClick={handler} — pass the function reference, never call it with ()
    • Props preview: components accept data via destructured parameters — function Card({ title, date }); full deep dive in Week 10

    Additional Resources

    Official Documentation