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:
- Single Parent Element: JSX expressions must have one root element
- camelCase Attributes: Use
classNamenotclass,onClicknotonclick - Self-Closing Tags: All tags must close:
<img />,<input /> - JavaScript Expressions: Use curly braces
{}to embed JavaScript - 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
keyprop 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
React Official Documentation - Components and Props: https://react.dev/learn/your-first-component - Comprehensive guide with interactive examples
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
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