JSX Deep Dive
Understanding JSX — syntax, expressions, conditional rendering, lists, fragments
JSX is Syntax Sugar
Every JSX element compiles to a React.createElement call.
// What you write:
const el = <h1 className="title">Hello</h1>
// What Babel produces:
const el = React.createElement("h1", { className: "title" }, "Hello")
JSX makes the tree readable. createElement is what the runtime actually runs.
JSX attributes use camelCase — className not class, htmlFor not for.
Expressions in JSX
Any JavaScript expression goes inside {}.
function Greeting({ user, unread }) {
const now = new Date().getHours()
const timeGreeting = now < 12 ? "Good morning" : "Good afternoon"
return (
<div>
<h1>
{timeGreeting}, {user.name}
</h1>
<p>
{user.name.length > 0 ? `You have ${unread} messages` : "No messages"}
</p>
<p>{Math.random().toFixed(2)}</p>
</div>
)
}
Statements (if, for, switch) don't fit inside {} — extract to a variable or use ternary.
Conditional Rendering
Different patterns for different situations.
Lists and Keys
Map over arrays to render lists. Every item needs a stable key.
interface Todo {
id: string
text: string
done: boolean
}
function TodoList({ items }: { items: Todo[] }) {
return (
<ul>
{items.map((item) => (
<li key={item.id}>
{item.text} {item.done ? "✓" : "○"}
</li>
))}
</ul>
)
}
Key requirements:
- Stable across re-renders — use unique IDs from your data
- Avoid
index— it breaks when items reorder, causing stale state
Only if the list is static and never reordered/filtered. Even then, prefer stable keys.
Fragments
Group elements without adding a DOM node.
function Columns() {
return (
<>
<td>First</td>
<td>Second</td>
</>
)
}
<> is shorthand for <React.Fragment>. Keyed fragments need the full <Fragment key={id}>.
Spread Props
Pass an object's properties as props with {...obj}.
interface User {
name: string
email: string
role: string
}
function ProfileCard({ name, email, role }: User) {
return (
<div className="card">
<h2>{name}</h2>
<p>{email}</p>
<Badge>{role}</Badge>
</div>
)
}
// Spread user object
const user: User = { name: "Alice", email: "a@b.com", role: "admin" }
;<ProfileCard {...user} />
See It In Action
1interface Member {2id: string3name: string4role: string5}6 7const team: Member[] = [8{ id: "m1", name: "Alice", role: "Designer" },9{ id: "m2", name: "Bob", role: "Developer" },10{ id: "m3", name: "Charlie", role: "Developer" },11]12 13function TeamList() {14const developers = team.filter(m => m.role === "Developer")15return (16 17<ul>18{developers.map(m => (19<li key={m.id}>{m.name} — {m.role}</li>20))}21</ul>22)23}24 25// Renders: Bob — Developer, Charlie — DeveloperFilters the team array to show only developers, then maps to JSX `<li>`elements. Each item uses a stable`key`(the member's`id`) so React can efficiently track changes in the list.