Ternary & Logical && in JSX
React JSX is just JavaScript, which means you can use all of JavaScript's inline expression operators to decide what renders. The two you'll use constantly are the ternary operator ? : and the logical AND &&. Understanding each one's behaviour — including their edge cases — is essential for writing correct, readable React.
The Ternary Operator: Two-Way Branches
The ternary condition ? ifTrue : ifFalse is the primary tool for choosing between two JSX outputs. Both branches are expressions, so the ternary can live directly inside JSX:
// Simple text choice
function SaveButton({ isSaving }) {
return (
<button disabled={isSaving}>
{isSaving ? 'Saving...' : 'Save'}
</button>
)
}
// Element choice
function Alert({ type, message }) {
return (
<div className={type === 'error' ? 'alert-error' : 'alert-info'}>
{type === 'error'
? <span>⚠ Error: {message}</span>
: <span>ℹ Info: {message}</span>
}
</div>
)
}
// Conditional rendering of a whole section
function UserCard({ user }) {
return (
<div>
<h2>{user.name}</h2>
{user.isAdmin
? <span className="badge">Administrator</span>
: <span className="badge">Member</span>
}
</div>
)
}Ternary with null for "Nothing"
Use null as one branch of the ternary when you want to render nothing for the false case:
function ErrorMessage({ error }) {
return (
<div>
{error ? <p className="error">{error}</p> : null}
</div>
)
}
// Equivalent — but && is more concise for single-branch cases
function ErrorMessage2({ error }) {
return (
<div>
{error && <p className="error">{error}</p>}
</div>
)
}The && Operator: Single-Branch Show/Hide
The logical AND && is JavaScript's "short-circuit" operator. If the left side is truthy, it returns the right side. If the left side is falsy, it returns the left side without evaluating the right side.
In JSX, returning false, null, or undefined renders nothing — so && becomes an elegant "show this element if condition is true":
function Inbox({ messages, hasNewMail }) {
return (
<div>
<h2>Inbox</h2>
{hasNewMail && <span className="badge">New!</span>}
{messages.length > 0 && (
<ul>
{messages.map(msg => (
<li key={msg.id}>{msg.subject}</li>
))}
</ul>
)}
</div>
)
}The 0 Gotcha — The Most Common && Bug
Here's the most dangerous && pitfall in React. When the left side evaluates to 0, JavaScript's && returns 0 — and React renders 0 to the DOM as a text node, which displays the digit "0" on screen.
// ✗ BUG: renders the number "0" when count is 0
function MessageBadge({ count }) {
return (
<span>
{count && <Badge>{count}</Badge>}
</span>
)
}
// With count=0: renders <span>0</span> — "0" appears on screen!
// ✓ Fix 1: coerce to boolean
{count > 0 && <Badge>{count}</Badge>}
// ✓ Fix 2: Boolean()
{Boolean(count) && <Badge>{count}</Badge>}
// ✓ Fix 3: double negation
{!!count && <Badge>{count}</Badge>}
// ✓ Fix 4: ternary (explicit null for falsy branch)
{count ? <Badge>{count}</Badge> : null}The || Operator: Fallback Values
The logical OR || returns the first truthy value. It's useful for displaying fallback content when a value is falsy:
function UserProfile({ user }) {
return (
<div>
<h2>{user.displayName || 'Anonymous'}</h2>
<p>{user.bio || 'No bio yet.'}</p>
{/* Careful: || treats "" (empty string) and 0 as falsy too */}
</div>
)
}
// user.displayName = '' → shows 'Anonymous' (treats empty string as falsy)
// user.displayName = null → shows 'Anonymous'
// user.displayName = 'Sarah' → shows 'Sarah'The ?? Operator: Null/Undefined Fallback
The nullish coalescing operator ?? is like || but only triggers for null and undefined — not for 0, "", or false:
function ProductCard({ product }) {
return (
<div>
{/* ?? is better here: a price of 0 is valid! */}
<span>${product.price ?? 'Price unavailable'}</span>
{/* || would wrongly show "Price unavailable" for price=0 */}
<span>${product.price || 'Price unavailable'}</span>
</div>
)
}
// product.price = 0 → ?? shows "$0", || shows "Price unavailable"
// product.price = null → ?? shows "Price unavailable", || same
// product.price = 9.99 → both show "$9.99"Nested Ternaries: When They Get Unreadable
Ternaries can be nested for multi-branch logic — but they quickly become impossible to read. Here's the inflection point:
// ✗ Hard to read — three-level nested ternary
function TrafficLight({ status }) {
return (
<div className={
status === 'red' ? 'light-red'
: status === 'yellow' ? 'light-yellow'
: status === 'green' ? 'light-green'
: 'light-off'
} />
)
}
// ✓ Much clearer — extract to a lookup object or variable
const COLOR_CLASS = {
red: 'light-red',
yellow: 'light-yellow',
green: 'light-green',
}
function TrafficLight({ status }) {
const className = COLOR_CLASS[status] ?? 'light-off'
return <div className={className} />
}
// ✓ Or extract with a dedicated function
function getStatusClass(status) {
if (status === 'red') return 'light-red'
if (status === 'yellow') return 'light-yellow'
if (status === 'green') return 'light-green'
return 'light-off'
}
function TrafficLight({ status }) {
return <div className={getStatusClass(status)} />
}Extracting Conditions to Variables
When JSX gets cluttered with inline conditions, move the logic above the return:
function DashboardHeader({ user, notifications, isMobile }) {
// Compute derived values before the JSX
const isAdmin = user?.role === 'admin'
const hasUnread = notifications.filter(n => !n.read).length > 0
const greeting = user ? `Welcome back, ${user.name}` : 'Welcome'
return (
<header>
<h1>{greeting}</h1>
{isAdmin && <span className="admin-badge">Admin</span>}
{hasUnread && <NotificationDot />}
{!isMobile && <SearchBar />}
</header>
)
}Optional Chaining in JSX
The optional chaining operator ?. pairs well with && and ?? when accessing deeply nested data:
function PostCard({ post }) {
return (
<article>
<h2>{post.title}</h2>
{post.author?.avatar && (
<img src={post.author.avatar} alt={post.author.name ?? 'Author'} />
)}
<p>{post.content?.slice(0, 200) ?? 'No content'}</p>
</article>
)
}&&— use for single-branch show/hide; always guard number values with> 0? :— use for two-branch choices; prefer over&&withnull||— use for string fallbacks when0and""should trigger the fallback??— use for fallbacks when0and""are valid values (only null/undefined triggers it)Nested ternaries — avoid beyond one level; extract to variables or functions