Understanding JSX
Master JSX syntax and learn how to write powerful, expressive React components.
Learning Objectives
- Understand what JSX is and why it exists
- Learn JSX syntax rules and best practices
- Master embedding JavaScript expressions in JSX
- Understand JSX attributes and event handling
- Learn JSX conditional rendering techniques
- Avoid common JSX pitfalls and errors
Understanding JSX
JSX (JavaScript XML) is a syntax extension for JavaScript that allows you to write HTML-like code in your React components. It's one of React's most distinctive features and makes building user interfaces intuitive and expressive.
What is JSX?
JSX is not HTML, even though it looks similar. It's a syntax extension that gets compiled to regular JavaScript function calls. Here's what JSX looks like:
// JSX const element = <h1>Hello, React!</h1>; // What it compiles to (simplified) const element = React.createElement('h1', null, 'Hello, React!');
Why JSX?
React separates concerns by components rather than technologies. JSX allows you to:
- Write declarative UI code that's easy to read and understand
- Keep markup and logic together in the same component
- Get compile-time error checking for your UI code
- Use the full power of JavaScript within your markup
JSX Syntax Rules
1. JSX Must Have One Parent Element
❌ Wrong:
function MyComponent() { return ( <h1>Title</h1> <p>Paragraph</p> // Error: Adjacent JSX elements ); }
✅ Correct:
function MyComponent() { return ( <div> <h1>Title</h1> <p>Paragraph</p> </div> ); } // Or use React Fragment function MyComponent() { return ( <> <h1>Title</h1> <p>Paragraph</p> </> ); }
2. Use className Instead of class
Since class
is a reserved word in JavaScript, JSX uses className
:
❌ Wrong:
<div class="container">Content</div>
✅ Correct:
<div className="container">Content</div>
3. Self-Closing Tags Must Be Closed
In HTML, some tags can be self-closing. In JSX, they must be closed:
❌ Wrong:
<img src="image.jpg" alt="Description"> <input type="text" name="username">
✅ Correct:
<img src="image.jpg" alt="Description" /> <input type="text" name="username" />
4. Attribute Names Use camelCase
HTML attributes in JSX use camelCase naming:
// HTML attributes become camelCase <input type="text" maxLength="50" autoComplete="off" tabIndex="1" /> // Event handlers are also camelCase <button onClick={handleClick}> Click me </button>
Embedding JavaScript Expressions
One of JSX's most powerful features is the ability to embed JavaScript expressions using curly braces {}
:
Basic Expressions
function Greeting() { const name = "Sarah"; const age = 25; return ( <div> <h1>Hello, {name}!</h1> <p>You are {age} years old.</p> <p>Next year you'll be {age + 1}.</p> </div> ); }
Function Calls
function UserProfile() { const user = { firstName: "John", lastName: "Doe" }; const getFullName = (user) => { return `${user.firstName} ${user.lastName}`; }; return ( <div> <h1>Welcome, {getFullName(user)}!</h1> <p>Today is {new Date().toLocaleDateString()}</p> </div> ); }
Object Properties
function ProductCard() { const product = { name: "Laptop", price: 999, image: "laptop.jpg", inStock: true }; return ( <div className="product-card"> <img src={product.image} alt={product.name} /> <h3>{product.name}</h3> <p>Price: ${product.price}</p> <p>Status: {product.inStock ? "In Stock" : "Sold Out"}</p> </div> ); }
JSX Attributes
String Attributes
// String literals use quotes <img src="avatar.jpg" alt="User avatar" /> <input type="email" placeholder="Enter your email" />
Expression Attributes
function UserAvatar() { const user = { name: "Alice", avatar: "alice.jpg", isOnline: true }; return ( <img src={user.avatar} alt={`\${user.name}'s avatar`} className={user.isOnline ? "online" : "offline"} width={50} height={50} /> ); }
Style Attribute
The style
attribute accepts a JavaScript object, not a string:
❌ Wrong:
<div style="color: red; font-size: 16px;">Text</div>
✅ Correct:
<div style={{ color: "red", fontSize: "16px", // camelCase for CSS properties backgroundColor: "blue", // kebab-case becomes camelCase marginTop: "10px" }}> Text </div> // Or define styles separately const styles = { container: { padding: "20px", border: "1px solid #ccc" } }; <div style={styles.container}>Content</div>
Conditional Rendering in JSX
Ternary Operator
function WelcomeMessage({ user }) { return ( <div> {user ? ( <h1>Welcome back, {user.name}!</h1> ) : ( <h1>Please sign in</h1> )} </div> ); }
Logical AND Operator
function Notifications({ notifications }) { return ( <div> <h2>Dashboard</h2> {notifications.length > 0 && ( <div className="notifications"> <p>You have {notifications.length} new notifications</p> </div> )} </div> ); }
Complex Conditional Logic
function UserStatus({ user }) { const renderStatus = () => { if (!user) { return <span className="status-offline">Offline</span>; } if (user.isActive) { return <span className="status-active">Active</span>; } if (user.lastSeen) { return <span className="status-away">Away</span>; } return <span className="status-unknown">Unknown</span>; }; return ( <div className="user-info"> <h3>{user?.name || "Guest"}</h3> {renderStatus()} </div> ); }
Rendering Lists
Basic List Rendering
function ShoppingList() { const items = ["Apples", "Bananas", "Oranges", "Milk"]; return ( <ul> {items.map((item, index) => ( <li key={index}>{item}</li> ))} </ul> ); }
Rendering Object Arrays
function UserList() { const users = [ { id: 1, name: "Alice", email: "alice@example.com" }, { id: 2, name: "Bob", email: "bob@example.com" }, { id: 3, name: "Charlie", email: "charlie@example.com" } ]; return ( <div className="user-list"> {users.map(user => ( <div key={user.id} className="user-card"> <h3>{user.name}</h3> <p>{user.email}</p> </div> ))} </div> ); }
Filtering Lists
function ActiveUsers({ users }) { const activeUsers = users.filter(user => user.isActive); return ( <div> <h2>Active Users ({activeUsers.length})</h2> {activeUsers.map(user => ( <div key={user.id} className="user-item"> <span>{user.name}</span> <span className="status-active">● Online</span> </div> ))} </div> ); }
Event Handling in JSX
Basic Event Handlers
function Button() { const handleClick = () => { alert("Button clicked!"); }; const handleMouseOver = (event) => { console.log("Mouse over:", event.target); }; return ( <button onClick={handleClick} onMouseOver={handleMouseOver} className="custom-button" > Click me! </button> ); }
Inline Event Handlers
function Counter() { const [count, setCount] = useState(0); return ( <div> <p>Count: {count}</p> <button onClick={() => setCount(count + 1)}> Increment </button> <button onClick={() => setCount(0)}> Reset </button> </div> ); }
Event Handler with Parameters
function NumberList() { const numbers = [1, 2, 3, 4, 5]; const handleNumberClick = (number) => { console.log(`Clicked number: ${number}`); }; return ( <div> {numbers.map(number => ( <button key={number} onClick={() => handleNumberClick(number)} > {number} </button> ))} </div> ); }
Common JSX Patterns
Fragment Shorthand
// Long form function Component() { return ( <React.Fragment> <h1>Title</h1> <p>Content</p> </React.Fragment> ); } // Short form function Component() { return ( <> <h1>Title</h1> <p>Content</p> </> ); }
Dynamic Component Rendering
function IconComponent({ type, size = 24 }) { const iconComponents = { home: HomeIcon, user: UserIcon, settings: SettingsIcon }; const IconToRender = iconComponents[type]; return IconToRender ? ( <IconToRender size={size} /> ) : ( <span>Unknown icon</span> ); }
Spread Attributes
function CustomInput(props) { const { label, ...inputProps } = props; return ( <div className="input-field"> <label>{label}</label> <input {...inputProps} /> </div> ); } // Usage <CustomInput label="Email" type="email" placeholder="Enter email" required />
Common JSX Mistakes and Solutions
1. Forgetting Keys in Lists
❌ Wrong:
{items.map(item => <li>{item}</li>)}
✅ Correct:
{items.map((item, index) => <li key={item.id || index}>{item}</li>)}
2. Using Index as Key (when order can change)
❌ Problematic:
{items.map((item, index) => <li key={index}>{item}</li>)}
✅ Better:
{items.map(item => <li key={item.id}>{item}</li>)}
3. Incorrect Boolean Rendering
❌ Wrong:
{count && <p>Count: {count}</p>} // Shows 0 when count is 0
✅ Correct:
{count > 0 && <p>Count: {count}</p>} {Boolean(count) && <p>Count: {count}</p>}
4. Mutating Props or State in JSX
❌ Wrong:
<ul> {items.reverse().map(item => <li key={item.id}>{item}</li>)} </ul>
✅ Correct:
<ul> {[...items].reverse().map(item => <li key={item.id}>{item}</li>)} </ul>
JSX vs createElement
Understanding what JSX compiles to helps you debug and understand React better:
// JSX <div className="container"> <h1>Hello, {name}!</h1> <button onClick={handleClick}> Click me </button> </div> // Compiled JavaScript React.createElement( 'div', { className: 'container' }, React.createElement('h1', null, 'Hello, ', name, '!'), React.createElement( 'button', { onClick: handleClick }, 'Click me' ) );
Best Practices
1. Keep JSX Simple and Readable
❌ Complex:
return ( <div> {user && user.posts && user.posts.length > 0 ? ( user.posts.filter(post => post.published).map(post => ( <div key={post.id}> <h3>{post.title.length > 50 ? post.title.substring(0, 50) + "..." : post.title}</h3> <p>{post.excerpt}</p> </div> )) ) : ( <p>No posts available</p> )} </div> );
✅ Clean:
const publishedPosts = user?.posts?.filter(post => post.published) || []; const truncateTitle = (title) => title.length > 50 ? title.substring(0, 50) + "..." : title; const renderPosts = () => { if (publishedPosts.length === 0) { return <p>No posts available</p>; } return publishedPosts.map(post => ( <div key={post.id}> <h3>{truncateTitle(post.title)}</h3> <p>{post.excerpt}</p> </div> )); }; return <div>{renderPosts()}</div>;
2. Use Descriptive Key Props
✅ Good:
{users.map(user => ( <UserCard key={`user-${user.id}`} user={user} /> ))}
3. Extract Complex Logic to Functions
✅ Good:
function UserProfile({ user }) { const getProfileCompleteness = () => { const fields = [user.name, user.email, user.avatar, user.bio]; const completedFields = fields.filter(Boolean).length; return Math.round((completedFields / fields.length) * 100); }; return ( <div> <h2>{user.name}</h2> <p>Profile {getProfileCompleteness()}% complete</p> </div> ); }
Summary
JSX is a powerful syntax extension that makes React components intuitive and expressive. Key takeaways:
✅ JSX compiles to JavaScript - it's not HTML
✅ Use curly braces to embed JavaScript expressions
✅ Follow JSX syntax rules (className, self-closing tags, etc.)
✅ Master conditional rendering with ternary operators and logical AND
✅ Handle lists properly with map() and unique keys
✅ Keep JSX clean by extracting complex logic to functions
Practice Exercises
Try building these components to practice JSX:
1. Weather Card
Create a component that shows weather information with conditional icons.
2. Product Catalog
Build a product list with filtering and search functionality.
3. User Profile
Create a user profile with conditional rendering based on user data.
4. Form Validation
Build a form with dynamic validation messages.
Next, we'll dive into React Components and learn how to build reusable, modular UI pieces that form the foundation of any React application!