You are viewing a preview of this lesson. Sign in to start learning
Back to React

Components & JSX

Build reusable components and understand JSX syntax

Components & JSX in React

Master React components and JSX syntax with free flashcards and spaced repetition practice. This lesson covers component creation, JSX syntax rules, props handling, and conditional renderingβ€”essential concepts for building modern React applications.

Welcome to React Components & JSX πŸ’»

Welcome to the foundation of React development! Components are the building blocks of every React application, and JSX is the syntax that makes writing them intuitive and powerful. Think of components as custom LEGO blocksβ€”you create reusable pieces that snap together to build complex user interfaces.

In this lesson, you'll learn how to create both functional and class components, write JSX that transforms into real DOM elements, pass data between components using props, and render dynamic content. By the end, you'll understand why React's component-based architecture has revolutionized frontend development.

Core Concepts

What Are Components? 🧩

Components are independent, reusable pieces of UI that encapsulate their own structure, styling, and behavior. They accept inputs called props (short for properties) and return React elements that describe what should appear on the screen.

There are two main types of components:

Component Type Syntax Use Case
Functional Components JavaScript functions Modern default (with Hooks)
Class Components ES6 classes Legacy code, complex lifecycle

Functional components are now the standard in modern React:

function Welcome(props) {
  return <h1>Hello, {props.name}!</h1>;
}

Class components use ES6 class syntax:

class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}!</h1>;
  }
}

πŸ’‘ Tip: Since React 16.8, functional components with Hooks are preferred for all new code. They're simpler, easier to test, and require less boilerplate.

Understanding JSX 🎨

JSX (JavaScript XML) is a syntax extension that lets you write HTML-like code directly in JavaScript. It looks like markup, but it's actually syntactic sugar for React.createElement() calls.

// This JSX:
const element = <h1 className="greeting">Hello, world!</h1>;

// Gets transformed to:
const element = React.createElement(
  'h1',
  {className: 'greeting'},
  'Hello, world!'
);

Key JSX Rules:

  1. Single Parent Element: JSX expressions must have one root element
  2. camelCase Attributes: Use className not class, onClick not onclick
  3. Self-Closing Tags: All tags must close: <img />, <input />
  4. JavaScript Expressions: Use curly braces {} to embed JavaScript
  5. Comments: Use {/* comment */} for JSX comments

🧠 Memory Device - FACES:

  • Fragments for multiple elements
  • Attributes use camelCase
  • Curly braces for expressions
  • Everything must close
  • Single root element

JSX vs HTML Differences πŸ”„

HTML JSX Reason
class className 'class' is reserved in JS
for htmlFor 'for' is reserved in JS
onclick onClick Event handlers are camelCase
style="color: red" style={{color: 'red'}} Style is an object
<br> <br /> All tags must self-close

Props: Passing Data to Components πŸ“¦

Props are how components communicate. They flow one-way from parent to child (like function arguments) and are read-only.

// Parent component passes props
function App() {
  return <UserCard name="Alice" age={28} isOnline={true} />;
}

// Child component receives props
function UserCard(props) {
  return (
    <div>
      <h2>{props.name}</h2>
      <p>Age: {props.age}</p>
      <p>Status: {props.isOnline ? 'Online' : 'Offline'}</p>
    </div>
  );
}

Destructuring Props (modern pattern):

function UserCard({ name, age, isOnline }) {
  return (
    <div>
      <h2>{name}</h2>
      <p>Age: {age}</p>
      <p>Status: {isOnline ? 'Online' : 'Offline'}</p>
    </div>
  );
}

πŸ’‘ Tip: String props can be passed without curly braces (name="Alice"), but all other types need them (age={28}, isOnline={true}).

Embedding Expressions in JSX πŸ”’

You can embed any valid JavaScript expression inside curly braces:

function MathDisplay() {
  const num1 = 10;
  const num2 = 20;
  const user = { firstName: 'Bob', lastName: 'Smith' };
  
  return (
    <div>
      <p>Sum: {num1 + num2}</p>
      <p>User: {user.firstName} {user.lastName}</p>
      <p>Random: {Math.floor(Math.random() * 100)}</p>
      <p>Time: {new Date().toLocaleTimeString()}</p>
    </div>
  );
}

What works in {}:

  • βœ… Variables: {name}
  • βœ… Expressions: {2 + 2}
  • βœ… Function calls: {getName()}
  • βœ… Ternary operators: {isTrue ? 'Yes' : 'No'}
  • βœ… Array methods: {items.map(item => ...)}
  • ❌ Statements (if/for/while)
  • ❌ Object literals directly (but okay as props)

Conditional Rendering πŸ”€

Since JSX doesn't support if/else statements directly, React developers use several patterns:

1. Ternary Operator (most common):

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

2. Logical AND (&&) for single condition:

function Mailbox({ unreadMessages }) {
  return (
    <div>
      <h1>Inbox</h1>
      {unreadMessages.length > 0 && (
        <h2>You have {unreadMessages.length} unread messages.</h2>
      )}
    </div>
  );
}

3. Variable assignment (complex logic):

function StatusBadge({ status }) {
  let badgeElement;
  
  if (status === 'success') {
    badgeElement = <span className="badge-green">βœ“ Success</span>;
  } else if (status === 'error') {
    badgeElement = <span className="badge-red">βœ— Error</span>;
  } else {
    badgeElement = <span className="badge-gray">β—‹ Pending</span>;
  }
  
  return <div>{badgeElement}</div>;
}

4. Early return pattern:

function UserProfile({ user }) {
  if (!user) {
    return <p>Loading...</p>;
  }
  
  return (
    <div>
      <h1>{user.name}</h1>
      <p>{user.email}</p>
    </div>
  );
}

Lists and Keys πŸ“‹

Rendering lists is common in React. Use JavaScript's map() method:

function TodoList({ todos }) {
  return (
    <ul>
      {todos.map((todo) => (
        <li key={todo.id}>{todo.text}</li>
      ))}
    </ul>
  );
}

⚠️ Keys are critical! They help React identify which items changed, were added, or removed. Keys must be:

  • Unique among siblings
  • Stable (don't use array indices if list can reorder)
  • Predictable (use IDs from your data)
// ❌ BAD: Using index (problematic if list reorders)
{items.map((item, index) => <li key={index}>{item}</li>)}

// βœ… GOOD: Using stable ID
{items.map((item) => <li key={item.id}>{item.name}</li>)}

Fragments: Avoiding Extra Divs 🎭

Components must return a single element, but sometimes you don't want extra wrapper divs:

// ❌ Extra div pollutes DOM
function Table() {
  return (
    <div>
      <td>Cell 1</td>
      <td>Cell 2</td>
    </div>
  );
}

// βœ… Fragment doesn't create extra DOM node
function Table() {
  return (
    <>
      <td>Cell 1</td>
      <td>Cell 2</td>
    </>
  );
}

// Long form (when you need a key)
import React from 'react';

function Table() {
  return (
    <React.Fragment>
      <td>Cell 1</td>
      <td>Cell 2</td>
    </React.Fragment>
  );
}

Component Composition πŸ—οΈ

Components can contain other components, creating a hierarchy:

function Avatar({ user }) {
  return <img src={user.avatarUrl} alt={user.name} />;
}

function UserInfo({ user }) {
  return (
    <div className="user-info">
      <Avatar user={user} />
      <div className="user-name">{user.name}</div>
    </div>
  );
}

function Comment({ author, text, date }) {
  return (
    <div className="comment">
      <UserInfo user={author} />
      <div className="comment-text">{text}</div>
      <div className="comment-date">{date}</div>
    </div>
  );
}

πŸ”§ Try this: Break down your UI into components. Good rule of thumb: if a part does one thing or gets reused, make it a component!

COMPONENT HIERARCHY VISUALIZATION

         App
          β”‚
    β”Œβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”
    β”‚     β”‚     β”‚
  Header Main Footer
          β”‚
    β”Œβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”
    β”‚     β”‚     β”‚
 Sidebar Posts Aside
          β”‚
    β”Œβ”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”
    β”‚     β”‚     β”‚
  Post  Post  Post
    β”‚
 β”Œβ”€β”€β”Όβ”€β”€β”
 β”‚  β”‚  β”‚
Title Body Comments

Examples with Explanations

Example 1: Product Card Component πŸ›οΈ

Let's build a realistic product card that demonstrates props, conditional rendering, and JSX expressions:

function ProductCard({ product, onAddToCart }) {
  // Destructure product properties
  const { name, price, image, inStock, discount } = product;
  
  // Calculate discounted price
  const finalPrice = discount ? price * (1 - discount) : price;
  
  return (
    <div className="product-card">
      <img src={image} alt={name} />
      
      <h3>{name}</h3>
      
      <div className="price-section">
        {discount ? (
          <>
            <span className="original-price">${price}</span>
            <span className="discounted-price">${finalPrice.toFixed(2)}</span>
            <span className="discount-badge">-{discount * 100}%</span>
          </>
        ) : (
          <span className="regular-price">${price.toFixed(2)}</span>
        )}
      </div>
      
      {inStock ? (
        <button onClick={() => onAddToCart(product)}>
          Add to Cart
        </button>
      ) : (
        <button disabled className="out-of-stock">
          Out of Stock
        </button>
      )}
    </div>
  );
}

// Usage:
function Shop() {
  const products = [
    { id: 1, name: 'Laptop', price: 999, image: '/laptop.jpg', inStock: true, discount: 0.1 },
    { id: 2, name: 'Mouse', price: 29, image: '/mouse.jpg', inStock: false, discount: null }
  ];
  
  const handleAddToCart = (product) => {
    console.log('Added:', product.name);
  };
  
  return (
    <div className="shop">
      {products.map(product => (
        <ProductCard 
          key={product.id}
          product={product}
          onAddToCart={handleAddToCart}
        />
      ))}
    </div>
  );
}

What's happening:

  • Props destructuring makes code cleaner (const { name, price } = product)
  • Ternary operators handle discount display logic
  • Conditional rendering shows different button states
  • Fragment (<>...</>) groups price elements without extra divs
  • Event handler passed as prop demonstrates component communication
  • key prop on mapped elements helps React track list items

Example 2: Dynamic Navigation Menu 🧭

function NavItem({ item, isActive }) {
  return (
    <li className={isActive ? 'nav-item active' : 'nav-item'}>
      <a href={item.url}>
        {item.icon && <span className="icon">{item.icon}</span>}
        {item.label}
        {item.badge && <span className="badge">{item.badge}</span>}
      </a>
    </li>
  );
}

function Navigation({ menuItems, currentPath }) {
  return (
    <nav>
      <ul className="nav-menu">
        {menuItems.map((item) => (
          <NavItem
            key={item.id}
            item={item}
            isActive={currentPath === item.url}
          />
        ))}
      </ul>
    </nav>
  );
}

// Usage:
function App() {
  const menuItems = [
    { id: 1, label: 'Home', url: '/', icon: '🏠' },
    { id: 2, label: 'Messages', url: '/messages', icon: 'πŸ’¬', badge: 3 },
    { id: 3, label: 'Profile', url: '/profile', icon: 'πŸ‘€' }
  ];
  
  return <Navigation menuItems={menuItems} currentPath="/messages" />;
}

Key concepts:

  • Conditional class names using ternary operator for active state
  • Optional rendering with && operator (icon and badge only if present)
  • Component composition (NavItem inside Navigation)
  • Dynamic styling based on props

Example 3: User Dashboard with Complex Conditional Logic πŸ“Š

function UserStats({ stats }) {
  if (!stats) {
    return <div className="loading">Loading stats...</div>;
  }
  
  return (
    <div className="stats-grid">
      {Object.entries(stats).map(([key, value]) => (
        <div key={key} className="stat-card">
          <h4>{key.replace('_', ' ').toUpperCase()}</h4>
          <p className="stat-value">{value}</p>
        </div>
      ))}
    </div>
  );
}

function UserDashboard({ user }) {
  // Early return for loading state
  if (!user) {
    return (
      <div className="dashboard loading">
        <p>Loading user data...</p>
      </div>
    );
  }
  
  // Early return for error state
  if (user.error) {
    return (
      <div className="dashboard error">
        <p>Error: {user.error}</p>
        <button>Retry</button>
      </div>
    );
  }
  
  // Main render
  return (
    <div className="dashboard">
      <header>
        <h1>Welcome, {user.name}!</h1>
        {user.isPremium && <span className="premium-badge">⭐ Premium</span>}
      </header>
      
      <UserStats stats={user.stats} />
      
      <section className="activity">
        <h2>Recent Activity</h2>
        {user.activities.length > 0 ? (
          <ul>
            {user.activities.slice(0, 5).map((activity, index) => (
              <li key={index}>{activity}</li>
            ))}
          </ul>
        ) : (
          <p className="empty-state">No recent activity</p>
        )}
      </section>
    </div>
  );
}

Advanced patterns shown:

  • Guard clauses (early returns) for loading/error states
  • Object.entries() to dynamically render object properties
  • String manipulation in JSX expressions
  • Array slicing to limit displayed items
  • Nested conditional rendering with ternary and &&
  • Multiple component levels demonstrating composition

Example 4: Form with Inline Styles and Dynamic Classes πŸ“

function FormField({ label, type, value, error, required }) {
  // Dynamic inline styles (use sparingly!)
  const inputStyle = {
    borderColor: error ? '#ff0000' : '#cccccc',
    backgroundColor: error ? '#fff5f5' : '#ffffff'
  };
  
  // Build class names dynamically
  const fieldClasses = [
    'form-field',
    error && 'has-error',
    required && 'required'
  ].filter(Boolean).join(' ');
  
  return (
    <div className={fieldClasses}>
      <label>
        {label}
        {required && <span className="required-mark">*</span>}
      </label>
      <input
        type={type}
        value={value}
        style={inputStyle}
        aria-invalid={error ? 'true' : 'false'}
      />
      {error && (
        <span className="error-message" role="alert">
          {error}
        </span>
      )}
    </div>
  );
}

// Usage:
function RegistrationForm() {
  return (
    <form>
      <FormField
        label="Email"
        type="email"
        value=""
        required={true}
        error="Please enter a valid email"
      />
      <FormField
        label="Password"
        type="password"
        value=""
        required={true}
        error={null}
      />
    </form>
  );
}

Notable techniques:

  • Inline styles as JavaScript objects (camelCase properties)
  • Dynamic class building with array and filter
  • Accessibility attributes (aria-invalid, role="alert")
  • Style conditional logic based on error state
  • Boolean filtering to clean up class names

πŸ€” Did you know? React processes around 1 million JSX transformations per second in large applications, making it incredibly efficient despite the syntax sugar!

Common Mistakes ⚠️

1. Forgetting to Close Tags

// ❌ WRONG
<img src="photo.jpg">
<input type="text">

// βœ… CORRECT
<img src="photo.jpg" />
<input type="text" />

Why: JSX requires all tags to be explicitly closed, unlike HTML where some tags can self-close implicitly.

2. Using class Instead of className

// ❌ WRONG
<div class="container">Content</div>

// βœ… CORRECT
<div className="container">Content</div>

Why: class is a reserved keyword in JavaScript, so JSX uses className instead.

3. Forgetting Curly Braces for Non-String Props

// ❌ WRONG
<UserCard age="25" isActive="true" />
// This passes strings "25" and "true", not number/boolean!

// βœ… CORRECT
<UserCard age={25} isActive={true} />

Why: Without curly braces, everything is treated as a string literal.

4. Rendering Objects Directly

const user = { name: 'Alice', age: 30 };

// ❌ WRONG - Error: Objects are not valid as a React child
<div>{user}</div>

// βœ… CORRECT
<div>{user.name}</div>
<div>{JSON.stringify(user)}</div>

Why: React can't render plain objects. You must extract specific properties or convert to string.

5. Missing Keys in Lists

// ❌ WRONG
{items.map(item => <li>{item}</li>)}

// ⚠️ PROBLEMATIC (index as key)
{items.map((item, index) => <li key={index}>{item}</li>)}

// βœ… CORRECT
{items.map(item => <li key={item.id}>{item.name}</li>)}

Why: Keys help React identify which items changed. Using indices can cause bugs when list reorders.

6. Mutating Props

function UserCard(props) {
  // ❌ WRONG - Never modify props!
  props.name = props.name.toUpperCase();
  
  return <h1>{props.name}</h1>;
}

// βœ… CORRECT
function UserCard(props) {
  const displayName = props.name.toUpperCase();
  return <h1>{displayName}</h1>;
}

Why: Props are read-only. Always create new variables for derived values.

7. Incorrect Comment Syntax

// ❌ WRONG
<div>
  // This won't work
  <!-- Neither will this -->
</div>

// βœ… CORRECT
<div>
  {/* This is a proper JSX comment */}
</div>

Why: JSX comments must be wrapped in curly braces with /* */ syntax.

8. Trying to Use If/Else in JSX

// ❌ WRONG - Statements don't work in JSX
<div>
  {if (isLoggedIn) {
    <p>Welcome!</p>
  }}
</div>

// βœ… CORRECT - Use ternary or &&
<div>
  {isLoggedIn ? <p>Welcome!</p> : null}
  {isLoggedIn && <p>Welcome!</p>}
</div>

Why: JSX only accepts expressions, not statements. Use ternary operators or logical AND.

Key Takeaways 🎯

βœ… Components are reusable, independent UI building blocks that accept props and return React elements

βœ… JSX is syntactic sugar for React.createElement() that looks like HTML but has important differences

βœ… Functional components are now the standardβ€”they're simpler and work with React Hooks

βœ… Props flow one-way from parent to child and are read-only (immutable)

βœ… Use curly braces {} to embed JavaScript expressions in JSX

βœ… Conditional rendering uses ternary operators, &&, or early returnsβ€”no if/else in JSX

βœ… Use className not class, htmlFor not for, and close all tags like <img />

βœ… When rendering lists, always provide a unique key prop for each item

βœ… Fragments <>...</> let you group elements without adding extra DOM nodes

βœ… Break complex UIs into smaller components for reusability and maintainability

πŸ“‹ Quick Reference Card

Create Component function MyComponent(props) { return <div>...</div>; }
Pass Props <MyComponent name="Alice" age={25} />
Destructure Props function MyComponent({ name, age }) { ... }
JavaScript Expression {variable}, {2 + 2}, {function()}
Conditional (ternary) {condition ? <A /> : <B />}
Conditional (AND) {condition && <Component />}
List Rendering {items.map(item => <li key={item.id}>{item.name}</li>)}
Fragment <>...</> or <React.Fragment>...</React.Fragment>
Comment {/* comment here */}
Inline Style style={{color: 'red', fontSize: '16px'}}
Class Name className="my-class"
Event Handler onClick={() => handleClick()}

πŸ“š Further Study

  1. React Official Documentation - Components and Props: https://react.dev/learn/your-first-component - Comprehensive guide with interactive examples

  2. MDN Web Docs - JSX In Depth: https://developer.mozilla.org/en-US/docs/Learn/Tools_and_testing/Client-side_JavaScript_frameworks/React_getting_started - Deep dive into JSX mechanics and best practices

  3. React TypeScript Cheatsheet: https://react-typescript-cheatsheet.netlify.app/docs/basic/getting-started/basic_type_example - Learn to add type safety to your React components