🏷️ JSX Syntax
In the last lesson you saw HTML-looking markup living right inside JavaScript functions. That is JSX, and it's the syntax you'll write in every React component from now on. It feels like HTML on purpose — but it follows JavaScript's rules, not the browser's. This lesson makes those differences explicit so JSX stops feeling like magic and starts feeling like a tool you control.
Week 4 · Monday: React Fundamentals · Lecture 2
🎯 Learning Objectives
By the end of this lesson, you will be able to:
- Explain what JSX is and what a build step compiles it into
- List the key differences between JSX and HTML (
className, camelCase, self-closing) - Embed JavaScript expressions in markup with curly braces
- Render lists with
.map()and uniquekeys - Choose the right conditional-rendering pattern (ternary,
&&, fragments) - Avoid the classic JSX beginner traps
Estimated Time: 60 minutes
Practice: Convert HTML to JSX and build a conditional notification component.
In This Lesson
What is JSX?
JSX (JavaScript XML) is a syntax extension that lets you write HTML-like markup directly inside JavaScript. Browsers don't understand JSX — a build tool called Babel (already wired up by Vite) compiles it into ordinary function calls before your code ever runs.
The analogy: JSX is like a chef's shorthand on a recipe card. It looks readable and familiar, but before anyone cooks, it's expanded into precise, literal steps.
<h1>Hello</h1>"] -->|Babel compiles| B["React.createElement
('h1', null, 'Hello')"] B -->|React renders| C[Real DOM node] C -->|Browser paints| D[Visible UI]
So this JSX:
const element = <h1 className="greeting">Hello, world!</h1>;
compiles to this plain JavaScript, which produces a plain object describing the UI:
const element = React.createElement(
'h1',
{ className: 'greeting' },
'Hello, world!'
);
// Which is really just an object:
// { type: 'h1', props: { className: 'greeting', children: 'Hello, world!' } }
💡 JSX is optional — but you'll always use it
You could call React.createElement by hand, but nobody does. JSX is far more readable, and because it compiles to a plain expression, you can treat a piece of JSX like any other value: store it in a variable, return it, or put it in an array.
JSX vs. HTML
JSX looks like HTML but follows JavaScript's rules. Here are the differences that matter day to day.
| Concept | HTML | JSX |
|---|---|---|
| CSS class | class="box" | className="box" |
| Event handler | onclick="fn()" | onClick={fn} |
| Label attribute | for="id" | htmlFor="id" |
| Void elements | <br>, <img> | <br />, <img /> (must close) |
| Inline style | style="color:red" | style={{ color: 'red' }} (object) |
1. className, not class
Because class is a reserved word in JavaScript, JSX uses className:
// HTML: <div class="container">Content</div>
<div className="container">Content</div>
2. camelCase attributes
Most DOM attributes become camelCase, and event handlers take a function, not a string:
// HTML: <div onclick="handleClick()" tabindex="0">
<div onClick={handleClick} tabIndex={0}>Click me</div>
3. Every tag must close
JSX is strict: void elements self-close, and every element you open must be closed.
<img src="photo.jpg" alt="A photo" /> {/* must self-close */}
<br />
<input type="text" />
4. Inline styles are objects
// Styles are a JS object with camelCase keys:
const boxStyle = { color: 'blue', backgroundColor: 'lightgray', padding: '10px' };
function Box() {
return <div style={boxStyle}>Styled with a JS object</div>;
}
// Or inline — note the DOUBLE braces: outer {} = "JS here", inner {} = the object
<div style={{ color: 'red', fontSize: '20px' }}>Inline styled</div>
Embedding JavaScript
The real power of JSX is dropping JavaScript expressions right into your markup with curly braces { }. Think of them as windows through which live JavaScript shines into your HTML.
function Greeting() {
const name = 'Sarah';
const hour = new Date().getHours();
return (
<div>
<h1>Hello, {name}!</h1> {/* a variable */}
<p>It's {hour} o'clock.</p>
<p>2 + 2 = {2 + 2}</p> {/* any expression */}
<p>Today is {new Date().toLocaleDateString()}</p>
</div>
);
}
⚠️ Expressions only — no statements
Curly braces accept anything that produces a value: variables, math, function calls, ternaries, .map(). They do not accept statements like if, for, or let. If you need branching, use a ternary (below) or compute the value above the return.
Because JSX itself is an expression, you can assign it to variables and return it from functions:
function getGreeting(user) {
if (user) {
return <h1>Hello, {user.name}!</h1>; // compute before return
}
return <h1>Hello, stranger!</h1>;
}
const button = <button>Click me</button>; // JSX in a variable
Conditional Rendering
Since you can't use an if statement inside JSX, React leans on JavaScript expressions to show or hide things.
Ternary — either/or
function Status({ isLoggedIn }) {
return (
<div>
{isLoggedIn ? <LogoutButton /> : <LoginButton />}
</div>
);
}
Logical && — show only when true
function Inbox({ notifications }) {
return (
<div>
{notifications.length > 0 && (
<span>You have {notifications.length} notifications</span>
)}
</div>
);
}
⚠️ The 0 trap with &&
If the left side of && is the number 0, React renders "0" on the screen instead of nothing. Guard against it: use notifications.length > 0 && … (a real boolean) rather than notifications.length && ….
Compute above the return — for anything complex
function Dashboard({ user }) {
let content;
if (!user) content = <LoginForm />;
else if (user.admin) content = <AdminPanel />;
else content = <UserHome />;
return <main>{content}</main>;
}
Lists & Keys
To render a list, map an array to an array of JSX elements. Each element needs a unique key so React can track which items changed, were added, or removed.
function TodoList({ todos }) {
return (
<ul>
{todos.map(todo => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
);
}
// Given:
const todos = [
{ id: 1, text: 'Learn JSX' },
{ id: 2, text: 'Render a list' },
{ id: 3, text: 'Add keys' },
];
key (an id), so React can update the list efficiently.⚠️ Don't use the array index as a key
Using key={index} seems easy, but it breaks when the list is reordered, filtered, or has items inserted — React mixes up which item is which. Use a stable, unique id from your data whenever possible.
Fragments & Safety
Return one root element
A component must return a single root. When you have siblings and don't want an extra wrapper <div> in the DOM, wrap them in a Fragment — the empty <>…</> tags:
function Header() {
return (
<>
<h1>Title</h1>
<p>Subtitle</p>
</>
);
}
// When a fragment needs a key (in a list), use the long form:
<React.Fragment key={item.id}>
<dt>{item.term}</dt>
<dd>{item.description}</dd>
</React.Fragment>
JSX escapes values automatically
Anything you put inside { } is rendered as text, never as live HTML. This protects you from cross-site scripting (XSS) by default:
const userInput = '<script>alert("XSS!")</script>';
// Safe — React shows the characters, it does NOT run the script:
<div>{userInput}</div>
// Renders the literal text <script>… on the page
✅ Comments in JSX
JSX comments go inside braces: {/* like this */}. A plain <!-- HTML comment --> won't work inside JSX.
Common Gotchas
1. Multiple elements need a wrapper
// ❌ Two roots — syntax error
function Wrong() {
return (
<h1>Title</h1>
<p>Text</p>
);
}
// ✅ Wrap in a fragment (or a real element)
function Right() {
return (
<>
<h1>Title</h1>
<p>Text</p>
</>
);
}
2. Expressions, not statements
// ❌ if is a statement — not allowed inside braces
<div>{if (ok) { return 'Yes' }}</div>
// ✅ Use a ternary expression
<div>{ok ? 'Yes' : 'No'}</div>
3. You can't render a raw object
const user = { name: 'John', age: 30 };
// ❌ "Objects are not valid as a React child"
<div>{user}</div>
// ✅ Render its properties (strings/numbers are fine)
<div>{user.name}, {user.age}</div>
What React tells you
Error: Objects are not valid as a React child
(found: object with keys {name, age}).
Fix: render user.name and user.age, or JSON.stringify(user).
Practice & Quiz
🏋️ Exercise 1: HTML → JSX
Goal: Convert this HTML into valid JSX inside a component.
<div class="card">
<img src="profile.jpg" alt="Profile">
<h3 class="name">John Doe</h3>
<p>Web Developer</p>
<button onclick="handleContact()">Contact</button>
</div>
💡 Hint
Four changes: class → className, self-close the <img>, onclick="…" → onClick={handleContact}, and wrap it all in a function that returns the JSX.
✅ Solution
function ProfileCard({ handleContact }) {
return (
<div className="card">
<img src="profile.jpg" alt="Profile" />
<h3 className="name">John Doe</h3>
<p>Web Developer</p>
<button onClick={handleContact}>Contact</button>
</div>
);
}
🏋️ Exercise 2: Conditional notification
Goal: Build a Notification component that shows an icon and message based on a type prop ('success' | 'error' | 'info').
function Notification({ type, message }) {
// TODO: pick an icon based on type, then render icon + message
}
💡 Hint
Compute the icon above the return using an object lookup: { success: '✅', error: '❌', info: 'ℹ️' }[type]. Fall back to a default with ?? 'ℹ️'.
✅ Solution
function Notification({ type, message }) {
const icon = { success: '✅', error: '❌', info: 'ℹ️' }[type] ?? 'ℹ️';
return (
<div className={`notification notification-${type}`}>
<span aria-hidden="true">{icon}</span> {message}
</div>
);
}
// <Notification type="success" message="Saved!" />
🎯 Quick Quiz
Question 1: Why does JSX use className instead of class?
Question 2: What can go inside JSX curly braces { }?
Question 3: When rendering a list, the key prop should be…
Best Practices & Pitfalls
✅ Do
- Use
className, camelCase attributes, and self-closing void tags - Keep logic above the
return; keep JSX readable - Give every list item a stable, unique
key - Use fragments
<>…</>to avoid needless wrapper<div>s
❌ Don't
- Put
if/forstatements inside curly braces — use expressions - Use the array index as a key in dynamic lists
- Render raw objects — pull out their string/number properties
- Guard with a number (
list.length && …) — it can print0
⚠️ Semantic HTML still matters
JSX gives you all of HTML. Reach for real semantic tags — <header>, <main>, <article>, <button> — not a wall of <div>s. Accessibility and SEO depend on it.
Summary
🎉 Key Takeaways
- JSX is HTML-like syntax in JavaScript; Babel compiles it to
React.createElementcalls - Key differences from HTML:
className, camelCase, self-closing tags, style objects - Embed any JavaScript expression with curly braces — not statements
- Render conditionally with ternaries and
&&; compute complex branches above the return - Map arrays to lists and give each item a stable, unique
key - Return one root (use a fragment); JSX escapes values to prevent XSS
📚 Additional Resources
- react.dev — Writing Markup with JSX
- react.dev — JavaScript in JSX with Curly Braces
- react.dev — Rendering Lists
🚀 What's Next?
You can now write JSX confidently. Next we turn that markup into reusable building blocks and learn how to feed them data: Components and Props — the heart of every React application.
🎉 JSX unlocked!
Every component you build from here is JSX plus a little JavaScript. You've got the syntax — now let's structure real components.