What is JSX?
JSX (JavaScript XML) is a syntax extension for JavaScript that allows you to write HTML-like code within your JavaScript. It's one of React's most distinctive features and a key part of what makes React development so intuitive.
JSX Example
// This is JSX
const element = <h1>Hello, world!</h1>;
Equivalent JavaScript without JSX
// This is what the above JSX compiles to
const element = React.createElement('h1', null, 'Hello, world!');
JSX provides a familiar syntax for defining what your UI should look like. If you know HTML, you already understand much of JSX. However, JSX is more powerful because it allows you to embed JavaScript expressions directly in your markup.
Analogy: JSX as a Blueprint Language
Think of JSX as a specialized blueprint language for architects:
- Regular HTML is like a basic floor plan drawn on paper - static and limited
- JSX is like an interactive 3D architectural model where you can not only visualize the structure but also define how it responds to different conditions (time of day, weather, number of occupants)
- Just as architects would find it more intuitive to design in a visual language rather than writing specs in plain text, developers find it more intuitive to define UI structures in JSX rather than raw JavaScript function calls
Why JSX?
While React doesn't require JSX, it offers several significant advantages:
- Familiarity: If you know HTML, JSX is easy to pick up
- Visual clarity: JSX clearly shows the structure of your UI
- Syntax checking: JSX errors can be caught during compilation
- Expressiveness: JSX allows seamless integration of JavaScript with markup
Facebook developed JSX for React because they believed that separating technologies (like templates and logic) creates artificial separation, while separating concerns (like UI components) creates more maintainable code.
JSX Fundamentals
Element Syntax
Basic Element
const element = <div>Hello World</div>;
Self-Closing Element
const image = <img src="profile.jpg" alt="Profile" />;
Nested Elements
const article = (
<article>
<h2>Article Title</h2>
<p>This is a paragraph of text.</p>
</article>
);
Important JSX Rules
- JSX elements must be wrapped in a single parent element (or fragment)
- All tags must be closed (either with closing tag or self-closing syntax)
- JSX attributes use camelCase naming convention (e.g.,
classNameinstead ofclass)
JSX vs HTML: Key Differences
| HTML | JSX | Reason |
|---|---|---|
class="container" |
className="container" |
"class" is a reserved keyword in JavaScript |
for="email" |
htmlFor="email" |
"for" is a reserved keyword in JavaScript |
onclick="handleClick()" |
onClick={handleClick} |
Camel case & passing functions as references |
tabindex="0" |
tabIndex={0} |
Camel case for consistency |
style="color: red;" |
style={{ color: 'red' }} |
JSX uses JavaScript objects for styles |
JavaScript Expressions in JSX
One of the most powerful features of JSX is the ability to embed JavaScript expressions directly in your markup. This enables dynamic content and logic within your components.
Embedding Variables
const name = 'John';
const greeting = <h1>Hello, {name}!</h1>;
Embedding Expressions
const product = { name: 'Laptop', price: 999.99 };
const productInfo = (
<div>
<h2>{product.name}</h2>
<p>Price: ${product.price.toFixed(2)}</p>
<p>With tax: ${(product.price * 1.08).toFixed(2)}</p>
</div>
);
Using Arrays
const colors = ['Red', 'Green', 'Blue'];
const colorList = (
<ul>
{colors.map(color => <li key={color}>{color}</li>)}
</ul>
);
What Can Go Inside Curly Braces
You can put any valid JavaScript expression inside the curly braces in JSX. This includes:
- Variables:
{name} - Property access:
{user.firstName} - Function calls:
{getFullName(user)} - Arithmetic:
{price * 1.08} - String concatenation:
{'Hello ' + name} - Ternary expressions:
{isLoggedIn ? 'Logout' : 'Login'} - Function expressions:
{() => handleAction()} - Array methods:
{items.map(item => <li>{item.name}</li>)}
What You Cannot Put in JSX Expressions
- Flow control statements like
if,for,while - Variable declarations with
var,let, orconst - Regular function or class declarations
For these cases, you need to use approaches like ternary operators, array methods, or write your logic outside the JSX.
Conditional Rendering in JSX
Since JSX is just JavaScript, we can use JavaScript's conditional operators to render different elements based on conditions.
Ternary Operator
function Greeting({ isLoggedIn }) {
return (
<div>
{isLoggedIn
? <h1>Welcome back!</h1>
: <h1>Please sign in.</h1>}
</div>
);
}
Logical && Operator
function Notifications({ messages }) {
return (
<div>
{messages.length > 0 && (
<h2>You have {messages.length} unread messages.</h2>
)}
</div>
);
}
Immediately-Invoked Function Expressions (IIFE)
For more complex conditional logic:
function WeatherInfo({ temperature }) {
return (
<div>
{(() => {
if (temperature < 32) return <p>It's freezing!</p>;
if (temperature < 70) return <p>It's cool.</p>;
return <p>It's warm!</p>;
})()}
</div>
);
}
Pre-processing Outside JSX
Often the cleanest approach for complex conditions:
function WeatherInfo({ temperature }) {
let message;
if (temperature < 32) {
message = <p>It's freezing!</p>;
} else if (temperature < 70) {
message = <p>It's cool.</p>;
} else {
message = <p>It's warm!</p>;
}
return (
<div>
{message}
</div>
);
}
Real-World Example: Conditional UI Elements
Consider a product listing where premium products have a special badge:
function ProductCard({ product }) {
return (
<div className="product-card">
<img src={product.image} alt={product.name} />
{/* Premium badge shown conditionally */}
{product.isPremium && (
<span className="premium-badge">Premium</span>
)}
<h3>{product.name}</h3>
{/* Different price display based on sale status */}
{product.onSale ? (
<div className="price-container">
<span className="regular-price">${product.regularPrice.toFixed(2)}</span>
<span className="sale-price">${product.salePrice.toFixed(2)}</span>
</div>
) : (
<div className="price-container">
<span className="regular-price">${product.regularPrice.toFixed(2)}</span>
</div>
)}
{/* Availability message */}
{product.inStock ? (
<span className="in-stock">In Stock</span>
) : (
<span className="out-of-stock">Out of Stock</span>
)}
</div>
);
}
Lists and Keys in JSX
When rendering lists of elements in React, we typically use array methods like map() to transform data into JSX elements. React requires a special key prop to efficiently update the UI when items change.
Basic List Rendering
function FruitList() {
const fruits = ['Apple', 'Banana', 'Cherry', 'Durian'];
return (
<ul>
{fruits.map(fruit => (
<li key={fruit}>{fruit}</li>
))}
</ul>
);
}
Keys and Why They Matter
Keys help React identify which items have changed, are added, or are removed. Keys should be unique among siblings.
Rules for Keys
- Keys must be unique among siblings (not globally)
- Stable identifiers (like IDs) are ideal for keys
- Array indices can be used as a last resort, but can cause issues if items reorder
- Keys don't get passed to components as props (use a different prop name if needed)
List Rendering with Complex Data
function UserList({ users }) {
return (
<div className="user-list">
{users.map(user => (
<div key={user.id} className="user-card">
<img src={user.avatar} alt={`${user.name}'s avatar`} />
<h3>{user.name}</h3>
<p>{user.email}</p>
<p>Joined: {new Date(user.joinDate).toLocaleDateString()}</p>
</div>
))}
</div>
);
}
Analogy: Keys as Name Tags
Think of React's keys like name tags at a conference:
- Without name tags, if people moved positions, you wouldn't know who was who at a glance
- With name tags, even if everyone rearranges themselves, you can still identify each person quickly
- Just as using duplicate name tags would be confusing at a conference, duplicate keys confuse React
- Just as unique IDs (like employee numbers) are better for identification than descriptive names (which might have duplicates), stable IDs make better keys than content values
JSX Attributes and Props
JSX elements can have attributes similar to HTML elements, but with some important differences. In React, these attributes are called "props" when they're passed to components.
HTML Attributes in JSX
// Basic attributes
const link = <a href="https://reactjs.org" target="_blank">React Website</a>;
// className instead of class
const container = <div className="container">Content</div>;
// htmlFor instead of for
const label = <label htmlFor="username">Username:</label>;
Dynamic Attributes
const imageUrl = "profile.jpg";
const username = "johndoe";
// Using variables in attributes
const profileImage = <img src={imageUrl} alt={`${username}'s profile`} />;
// Conditional attributes
const button = <button disabled={!isActive}>Click Me</button>;
Spreading Attributes
When you have an object containing multiple properties that you want to pass as props:
const buttonProps = {
disabled: false,
className: 'primary-button',
onClick: handleClick
};
// Using the spread operator
const button = <button {...buttonProps}>Click Me</button>;
Custom Attributes with data-*
// Custom data attributes work the same as in HTML
const listItem = <li data-testid="item" data-category="fruit">Apple</li>;
Styling with JSX
Using className
// Using CSS classes
const header = <header className="app-header dark-theme">My App</header>;
Inline Styles
// Inline styles use objects with camelCase properties
const styles = {
backgroundColor: 'black',
color: 'white',
padding: '10px',
borderRadius: '4px'
};
const button = <button style={styles}>Dark Button</button>;
// Inline style object directly in JSX
const inlineButton = (
<button
style={{
backgroundColor: 'blue',
color: 'white',
padding: '10px'
}}
>
Blue Button
</button>
);
CSS-in-JS Libraries
Many React projects use CSS-in-JS libraries like styled-components or emotion for more powerful styling options:
// Using styled-components
import styled from 'styled-components';
const Button = styled.button`
background-color: ${props => props.primary ? 'blue' : 'gray'};
color: white;
padding: 10px 15px;
border-radius: 4px;
border: none;
cursor: pointer;
&:hover {
opacity: 0.8;
}
`;
// Usage
const StyledButtonExample = () => (
<div>
<Button primary>Primary Button</Button>
<Button>Secondary Button</Button>
</div>
);
JSX Fragments
React requires that JSX elements have a single parent. But sometimes you don't want to add an extra DOM element just for this purpose. React Fragments solve this problem.
The Problem with Wrapper Elements
// Adding extra div just for JSX syntax requirements
function ListItems() {
return (
<div> {/* Extra div that might break styling */}
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</div>
);
}
Solution: React.Fragment
import React from 'react';
function ListItems() {
return (
<React.Fragment>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</React.Fragment>
);
}
Shorthand Fragment Syntax
function ListItems() {
return (
<>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</>
);
}
Fragments with Keys
When using fragments in a list, you might need to assign keys. In this case, you can't use the shorthand syntax:
function GroceryList({ items }) {
return (
<dl>
{items.map(item => (
// Can't use <></> here because we need to specify a key
<React.Fragment key={item.id}>
<dt>{item.name}</dt>
<dd>{item.description}</dd>
</React.Fragment>
))}
</dl>
);
}
Real-World Use Case: Table Rows
Fragments are particularly useful for tables, where you can't have divs between tr elements:
function UserTable({ users }) {
return (
<table>
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Email</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{users.map(user => (
<tr key={user.id}>
<td>{user.id}</td>
<td>{user.name}</td>
<td>{user.email}</td>
<td>
<>
<button onClick={() => editUser(user.id)}>Edit</button>
<button onClick={() => deleteUser(user.id)}>Delete</button>
</>
</td>
</tr>
))}
</tbody>
</table>
);
}
JSX Comments
Comments in JSX need to be placed inside curly braces, using JavaScript's comment syntax.
function CommentExample() {
return (
<div>
{/* This is a comment in JSX */}
<h1>Hello World</h1>
{/*
Multi-line comments
work too!
*/}
{/* You can put expressions in comments: {2 + 2} */}
{/*
JSX-specific gotchas:
- Don't use HTML comments: <!-- This doesn't work -->
- Make sure to close your braces: { /* This causes an error }
*/}
</div>
);
}
JSX Under the Hood
To truly understand JSX, it's important to know what's happening behind the scenes. JSX is ultimately transpiled into regular JavaScript before it runs in the browser.
The React.createElement Function
When Babel transpiles JSX, it converts it to calls to React.createElement().
// The React.createElement() function takes 3 arguments:
React.createElement(
type, // Either a string (for DOM elements) or a component
props, // An object of properties/attributes or null
...children // The content inside the element
);
// Example transformations:
// JSX:
<button className="btn" onClick={handleClick}>
Click me
</button>
// Transpiled JavaScript:
React.createElement(
'button',
{ className: 'btn', onClick: handleClick },
'Click me'
);
// JSX with children:
<div>
<h1>Title</h1>
<p>Paragraph</p>
</div>
// Transpiled JavaScript:
React.createElement(
'div',
null,
React.createElement('h1', null, 'Title'),
React.createElement('p', null, 'Paragraph')
);
Analogy: Architectural Blueprints
Think of the relationship between JSX and React.createElement() like this:
- JSX is like an architectural blueprint with intuitive visual representation of the structure
- React.createElement() is like the technical specifications and measurements - same information but in a format that's harder for humans to visualize
- Babel is the architect who translates between the visual plans and the technical specs
While technically you could write applications using just React.createElement() calls, it would be like trying to design a building using only numerical measurements without diagrams - possible, but much more difficult to visualize and maintain.
Why It Matters
Understanding this transformation helps explain:
- Why we need to import React even when not directly using it (pre-React 17)
- Why JSX has different attribute names for certain HTML attributes
- How JSX expressions get evaluated to JavaScript values
- How React's component model works with JSX
React 17+ JSX Transformation
React 17 introduced a new JSX transformation that doesn't require importing React just to use JSX:
// Before React 17 (old transform)
import React from 'react';
function App() {
return <h1>Hello World</h1>;
}
// After React 17 (new transform)
// No import needed for JSX!
function App() {
return <h1>Hello World</h1>;
}
The new transform uses functions from a new package called `react/jsx-runtime` that is automatically imported by the compiler.
JSX Best Practices
Organization and Readability
Use parentheses for multi-line JSX
// Good - easier to read with parentheses
return (
<div>
<h1>Title</h1>
<p>Content</p>
</div>
);
// Bad - hard to read without parentheses
return <div>
<h1>Title</h1>
<p>Content</p>
</div>;
Destructure props for clarity
// Good - destructured props
function UserProfile({ name, email, avatar }) {
return (
<div>
<img src={avatar} alt={name} />
<h2>{name}</h2>
<p>{email}</p>
</div>
);
}
// Less clear
function UserProfile(props) {
return (
<div>
<img src={props.avatar} alt={props.name} />
<h2>{props.name}</h2>
<p>{props.email}</p>
</div>
);
}
Logic and Structure
Extract complex logic from JSX
// Good - complex logic extracted
function ProductGrid({ products, filter }) {
// Complex filtering logic extracted from JSX
const filteredProducts = useMemo(() => {
return products
.filter(product => product.category === filter.category)
.filter(product => product.price <= filter.maxPrice)
.sort((a, b) => a.price - b.price);
}, [products, filter]);
return (
<div className="grid">
{filteredProducts.map(product => (
<ProductCard key={product.id} product={product} />
))}
</div>
);
}
// Bad - complex logic inside JSX
function ProductGrid({ products, filter }) {
return (
<div className="grid">
{products
.filter(product => product.category === filter.category)
.filter(product => product.price <= filter.maxPrice)
.sort((a, b) => a.price - b.price)
.map(product => (
<ProductCard key={product.id} product={product} />
))
}
</div>
);
}
Use logical && operator carefully
// Potential bug - if count is 0, renders "0" instead of nothing
function Notifications({ count }) {
return (
<div>
{count && <p>You have {count} notifications</p>}
</div>
);
}
// Better approach - be explicit
function Notifications({ count }) {
return (
<div>
{count > 0 && <p>You have {count} notifications</p>}
</div>
);
}
Performance Considerations
Avoid creating functions in JSX
// Bad - creates a new function on every render
function Button({ text, id }) {
return (
<button onClick={() => console.log(`Button ${id} clicked`)}>
{text}
</button>
);
}
// Better - define the function outside JSX
function Button({ text, id }) {
const handleClick = () => {
console.log(`Button ${id} clicked`);
};
return (
<button onClick={handleClick}>
{text}
</button>
);
}
Use keys properly
// Bad - using index as key
function TodoList({ todos }) {
return (
<ul>
{todos.map((todo, index) => (
<li key={index}>{todo.text}</li>
))}
</ul>
);
}
// Good - using stable ID as key
function TodoList({ todos }) {
return (
<ul>
{todos.map(todo => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
);
}
Real-World JSX Example: Building a Dashboard Card
Let's put together what we've learned by building a realistic UI component using JSX. We'll create a dashboard card that displays different information based on its type.
import React from 'react';
import './DashboardCard.css';
function DashboardCard({
type,
title,
data,
icon,
trend,
loading,
error
}) {
// Handle different loading/error states
if (loading) {
return (
<div className="dashboard-card dashboard-card--loading">
<div className="loading-spinner"></div>
</div>
);
}
if (error) {
return (
<div className="dashboard-card dashboard-card--error">
<div className="error-icon">⚠️</div>
<p>{error}</p>
</div>
);
}
// Format the data based on card type
const formattedData = formatCardData(type, data);
// Determine trend class and icon
const trendClass = trend > 0
? 'trend--positive'
: trend < 0
? 'trend--negative'
: 'trend--neutral';
const trendIcon = trend > 0
? '↑'
: trend < 0
? '↓'
: '→';
return (
<div className={`dashboard-card dashboard-card--${type}`}>
<div className="card-header">
<h3 className="card-title">{title}</h3>
{icon && <div className="card-icon">{icon}</div>}
</div>
<div className="card-content">
{/* Different content based on card type */}
{type === 'stat' && (
<>
<div className="stat-value">{formattedData.value}</div>
{trend !== undefined && (
<div className={`trend ${trendClass}`}>
{trendIcon} {Math.abs(trend)}%
</div>
)}
</>
)}
{type === 'list' && (
<ul className="data-list">
{formattedData.items.map(item => (
<li key={item.id} className="data-list-item">
<span className="item-label">{item.label}</span>
<span className="item-value">{item.value}</span>
</li>
))}
</ul>
)}
{type === 'chart' && (
<div className="chart-container">
{/* Chart component would go here */}
{formattedData.chartComponent}
</div>
)}
</div>
{/* Optional footer */}
{formattedData.footer && (
<div className="card-footer">
{formattedData.footer}
</div>
)}
</div>
);
}
// Helper function to format card data
function formatCardData(type, data) {
switch (type) {
case 'stat':
return {
value: typeof data === 'number'
? data.toLocaleString()
: data
};
case 'list':
return {
items: data.map(item => ({
id: item.id,
label: item.label,
value: typeof item.value === 'number'
? item.value.toLocaleString()
: item.value
}))
};
case 'chart':
return {
chartComponent: data.component,
footer: data.footer
};
default:
return { value: data };
}
}
export default DashboardCard;
Using the Component
import React from 'react';
import DashboardCard from './DashboardCard';
import SalesChart from './SalesChart';
function Dashboard() {
return (
<div className="dashboard">
{/* Stats Card */}
<DashboardCard
type="stat"
title="Total Revenue"
data={42589.75}
icon="💰"
trend={5.4}
/>
{/* List Card */}
<DashboardCard
type="list"
title="Top Products"
data={[
{ id: 1, label: 'Product A', value: 12345 },
{ id: 2, label: 'Product B', value: 8765 },
{ id: 3, label: 'Product C', value: 5432 }
]}
icon="📊"
/>
{/* Chart Card */}
<DashboardCard
type="chart"
title="Sales Trend"
data={{
component: <SalesChart data={salesData} />,
footer: "Last 30 days"
}}
/>
{/* Loading State */}
<DashboardCard
type="stat"
title="Visitors"
loading={true}
/>
{/* Error State */}
<DashboardCard
type="stat"
title="Conversion Rate"
error="Failed to load data"
/>
</div>
);
}
export default Dashboard;
This example demonstrates many JSX concepts:
- Conditional rendering for different states (loading, error, different card types)
- Dynamic class names with template literals
- List rendering with keys
- JSX fragments to avoid unnecessary wrapper elements
- Conditional logic both inside and outside the JSX
- Destructuring props for cleaner code
Practice Activities
Activity 1: JSX Conversion
Objective: Practice converting between HTML and JSX.
Instructions:
- Take the following HTML snippet and convert it to valid JSX:
<!-- Convert this HTML to JSX -->
<div class="user-profile">
<img src="profile.jpg" alt="User Profile" class="profile-image">
<div class="user-details">
<h2>John Doe</h2>
<p>Web Developer</p>
<label for="user-status">Status:</label>
<select id="user-status">
<option value="active">Active</option>
<option value="away">Away</option>
</select>
<button onclick="editProfile()">Edit Profile</button>
</div>
</div>
Activity 2: Dynamic JSX
Objective: Practice using JavaScript expressions in JSX.
Instructions:
- Create a component called
ProductCardthat receives the following props: product: An object with properties: id, name, price, discountPercentage, and inStock- The component should:
- Display the product name as a heading
- If there's a discount, show both the original price (struck through) and the discounted price
- If there's no discount, just show the price
- Show "In Stock" in green or "Out of Stock" in red based on the inStock property
- Include a "Add to Cart" button that is disabled if the product is out of stock
Activity 3: List Rendering
Objective: Practice rendering lists with JSX and keys.
Instructions:
- Create a component called
TaskListthat: - Accepts an array of task objects with properties: id, text, priority, and completed
- Renders the tasks as a list of items
- Color-codes tasks by priority (high: red, medium: orange, low: green)
- Strikes through completed tasks
- Includes a summary showing counts of completed and remaining tasks
- Ensure proper use of keys and conditional rendering
Resources for Further Learning
Official Documentation:
- Introducing JSX - React's official guide
- JSX In Depth - Advanced JSX concepts
Interactive Learning:
- Scrimba React Course - Interactive tutorials
- React Docs Beta - Learn React - New interactive docs
Additional Resources:
- Babel REPL - See how JSX transpiles to JavaScript
- ESLint Plugin React - Rules for JSX best practices
Summary
- JSX is a syntax extension to JavaScript that resembles HTML but has additional capabilities
- JSX gets transpiled to
React.createElement()calls by Babel before execution - JSX allows embedding JavaScript expressions using curly braces
{} - JSX uses camelCase property naming convention for attributes (e.g.,
classNameinstead ofclass) - React requires a unique
keyprop when rendering lists to efficiently update the DOM - Conditional rendering in JSX can be done using ternary operators, logical && operator, or by assigning elements to variables
- React Fragments (
<></>or<React.Fragment>) let you group elements without adding extra DOM nodes - JSX attributes can be passed dynamically and spread using the
{...props}syntax - Understanding JSX is fundamental to React development and creates more readable, maintainable code
In the next lecture, we'll dive into React components, understanding both functional and class components and how they work with props.