Back to Tutorial
Getting Started with React
Theory Lesson
20 min

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!