JSX Basics
JSX - JavaScript XML
JSX is a syntax extension for JavaScript that lets you write HTML-like code in your JavaScript files. It’s one of React’s most distinctive features.
What is JSX?
JSX looks like HTML but it’s actually JavaScript:
const element = <h1>Hello, world!</h1>;
This is neither a string nor HTML - it’s JSX! Babel compiles it to JavaScript:
// What you write
const element = <h1>Hello, world!</h1>;
// What it becomes
const element = React.createElement('h1', null, 'Hello, world!');
Why JSX?
1. Familiar syntax - Looks like HTML you already know
2. Visual - Easy to see the UI structure
3. Powerful - Full JavaScript inside your markup
4. Type-safe - Errors caught at compile time
Basic JSX Syntax
Embedding Expressions:
Use curly braces {} for JavaScript expressions:
const name = "Alice";
const element = <h1>Hello, {name}!</h1>;
const a = 5;
const b = 10;
const math = <p>Sum: {a + b}</p>;
const user = { firstName: "Bob", lastName: "Smith" };
const fullName = <h2>{user.firstName} {user.lastName}</h2>;
JSX is an expression:
You can use JSX anywhere you’d use a JavaScript expression:
// Assign to variable
const greeting = <h1>Hello!</h1>;
// Return from function
function getGreeting() {
return <h1>Hello!</h1>;
}
// Use in conditions
const element = isLoggedIn ? <UserGreeting /> : <GuestGreeting />;
// Store in array
const elements = [<div>First</div>, <div>Second</div>];
JSX Attributes
Attributes use camelCase:
// HTML
<div class="container" tabindex="0">
// JSX
<div className="container" tabIndex="0">
Common attribute differences:
| HTML | JSX |
|---|---|
class |
className |
for |
htmlFor |
tabindex |
tabIndex |
onclick |
onClick |
Dynamic attributes:
const altText = "Profile picture";
const imgUrl = "/images/avatar.jpg";
const image = <img src={imgUrl} alt={altText} />;
// Conditional classes
const buttonClass = isActive ? "btn-active" : "btn-inactive";
const button = <button className={buttonClass}>Click</button>;
Children in JSX
Text children:
<h1>Hello, world!</h1>
Element children:
<div>
<h1>Title</h1>
<p>Paragraph</p>
</div>
Expression children:
<div>
{user.name}
{2 + 2}
{getMessage()}
</div>
Mixed children:
<p>
Hello, {user.name}! You have {messageCount} messages.
</p>
Single Root Element
JSX must have one parent element:
// ❌ Wrong - multiple roots
return (
<h1>Title</h1>
<p>Text</p>
);
// ✅ Right - wrapped in div
return (
<div>
<h1>Title</h1>
<p>Text</p>
</div>
);
// ✅ Right - using Fragment
return (
<>
<h1>Title</h1>
<p>Text</p>
</>
);
// ✅ Right - explicit Fragment
return (
<React.Fragment>
<h1>Title</h1>
<p>Text</p>
</React.Fragment>
);
Fragments (<>...</>) let you group elements without adding extra DOM nodes.
JavaScript in JSX
Any JavaScript expression works:
// Variables
<div>{userName}</div>
// Math
<div>{price * quantity}</div>
// Function calls
<div>{formatDate(new Date())}</div>
// Ternary operators
<div>{isOnline ? "Online" : "Offline"}</div>
// Logical AND
<div>{isLoggedIn && <UserMenu />}</div>
// Array methods
<ul>
{items.map(item => <li>{item}</li>)}
</ul>
Statements don’t work:
// ❌ Wrong - can't use if statements
<div>
{if (isLoggedIn) { return "Welcome" }}
</div>
// ✅ Right - use ternary
<div>
{isLoggedIn ? "Welcome" : "Please log in"}
</div>
// ✅ Right - use function
<div>
{getGreeting(isLoggedIn)}
</div>
Conditional Rendering
1. Ternary operator (if/else):
function Greeting({ isLoggedIn }) {
return (
<div>
{isLoggedIn ? (
<h1>Welcome back!</h1>
) : (
<h1>Please sign in</h1>
)}
</div>
);
}
2. Logical AND (if only):
function Mailbox({ unreadMessages }) {
return (
<div>
<h1>Your Mailbox</h1>
{unreadMessages.length > 0 && (
<p>You have {unreadMessages.length} unread messages</p>
)}
</div>
);
}
3. Variable assignment:
function Greeting({ isLoggedIn }) {
let message;
if (isLoggedIn) {
message = <h1>Welcome back!</h1>;
} else {
message = <h1>Please sign in</h1>;
}
return <div>{message}</div>;
}
Rendering Lists
Use .map() to render arrays:
function TodoList() {
const todos = [
{ id: 1, text: 'Learn React' },
{ id: 2, text: 'Build app' },
{ id: 3, text: 'Deploy' }
];
return (
<ul>
{todos.map(todo => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
);
}
Keys are important:
// ✅ Good - unique, stable ID
{items.map(item => <div key={item.id}>{item.name}</div>)}
// ⚠️ OK for static lists only
{items.map((item, index) => <div key={index}>{item}</div>)}
// ❌ Bad - will cause warnings
{items.map(item => <div>{item}</div>)}
Inline Styles
Styles are objects with camelCase properties:
const divStyle = {
color: 'blue',
backgroundColor: 'lightgray',
padding: '10px',
borderRadius: '5px'
};
<div style={divStyle}>Styled content</div>
// Or inline
<div style={{ color: 'red', fontSize: '20px' }}>
Red text
</div>
Note: CSS property names use camelCase in JSX:
background-color→backgroundColorfont-size→fontSizeborder-radius→borderRadius
Comments in JSX
return (
<div>
{/* This is a comment */}
<h1>Title</h1>
{/*
Multi-line comment
in JSX
*/}
<p>Text</p>
</div>
);
Self-Closing Tags
Tags with no children must be self-closing:
// ❌ Wrong
<img src="photo.jpg">
<br>
<input type="text">
// ✅ Right
<img src="photo.jpg" />
<br />
<input type="text" />
Preventing Injection Attacks
JSX escapes values by default (safe from XSS):
const userInput = '<script>alert("hacked")</script>';
// Safe - renders as text, not executed
<div>{userInput}</div>
// Shows: <script>alert("hacked")</script>
To render HTML (dangerous!), use dangerouslySetInnerHTML:
// Only use with trusted content!
<div dangerouslySetInnerHTML={{ __html: trustedHTML }} />
Practical Examples
Example 1: User Card
function UserCard({ user }) {
return (
<div className="user-card">
<img
src={user.avatar}
alt={`${user.name}'s avatar`}
className="avatar"
/>
<h3>{user.name}</h3>
<p className="email">{user.email}</p>
{user.isVerified && (
<span className="badge">✓ Verified</span>
)}
</div>
);
}
Example 2: Product List
function ProductList({ products }) {
return (
<div className="product-list">
{products.length === 0 ? (
<p>No products found</p>
) : (
<div className="grid">
{products.map(product => (
<div key={product.id} className="product-card">
<img src={product.image} alt={product.name} />
<h4>{product.name}</h4>
<p className="price">${product.price}</p>
{product.inStock ? (
<button>Add to Cart</button>
) : (
<p className="out-of-stock">Out of Stock</p>
)}
</div>
))}
</div>
)}
</div>
);
}
Example 3: Status Indicator
function StatusIndicator({ status }) {
const statusConfig = {
online: { color: 'green', text: 'Online' },
away: { color: 'yellow', text: 'Away' },
offline: { color: 'gray', text: 'Offline' }
};
const config = statusConfig[status];
return (
<div className="status">
<span
className="dot"
style={{ backgroundColor: config.color }}
/>
<span>{config.text}</span>
</div>
);
}
JSX Best Practices
1. Keep it readable:
// Good - readable
return (
<div>
<Header />
<Content />
<Footer />
</div>
);
// Bad - hard to read
return <div><Header /><Content /><Footer /></div>;
2. Extract complex JSX:
function UserProfile({ user }) {
const profileHeader = (
<div className="header">
<img src={user.avatar} alt={user.name} />
<h2>{user.name}</h2>
</div>
);
return (
<div className="profile">
{profileHeader}
<UserDetails user={user} />
</div>
);
}
3. Use meaningful keys:
// Good
{users.map(user => <User key={user.id} {...user} />)}
// Bad
{users.map((user, i) => <User key={i} {...user} />)}
Common Mistakes
1. Forgetting curly braces:
// Wrong
<div>userName</div> // Shows "userName"
// Right
<div>{userName}</div> // Shows the value
2. Using class instead of className:
// Wrong
<div class="container">
// Right
<div className="container">
3. Missing keys in lists:
// Wrong - console warning
{items.map(item => <div>{item}</div>)}
// Right
{items.map(item => <div key={item.id}>{item}</div>)}
Next Article: Components