Real interfaces change with the situation. A visitor who is logged in sees “My account”; a guest sees “Log in”. An empty cart shows a friendly message; a full one shows a total. This is called React conditional rendering, and you do it with ordinary JavaScript – if, the ternary operator and &&. There is no special template syntax to learn.
Using if with an early return
Because a component is a normal function, you can use an if statement before the return. This is the clearest option when the whole output changes. A common pattern is the early return: handle the special case first and return, then write the normal case below without extra nesting.
function OrderStatus({ order }) {
if (!order) {
return <p>No order found.</p>;
}
if (order.cancelled) {
return <p>Order #{order.id} was cancelled.</p>;
}
return <p>Order #{order.id} will arrive on {order.date}.</p>;
}Remember that you cannot put an if statement inside JSX braces, because braces only accept expressions. For small changes inside the markup, use the two expression forms below.
Early returns are especially handy for loading and error screens. A component that shows data can first check “is it still loading?” and return a spinner, then check “did it fail?” and return a message, and only then render the real content. You will use exactly this shape in React useEffect when you fetch data from a server.
The ternary operator: this or that
The conditional (ternary) operator condition ? a : b is an expression, so it fits inside JSX. Use it when you need to show one of two things, for example a button label or a class name.
Logical &&: this or nothing
When you want to show something or nothing, use condition && <Element />. If the condition is true, JavaScript returns the element on the right; if it is false, it returns false, and React renders nothing for false.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>A login toggle with a ternary and logical AND</title>
<script src="https://unpkg.com/@babel/[email protected]/babel.min.js"></script>
<style>
body { font-family: system-ui, sans-serif; padding: 16px; line-height: 1.5; }
.bar { display: flex; justify-content: space-between; align-items: center; background: #f1f5f9; padding: 8px 12px; border-radius: 8px; }
.alert { background: #fff4e5; border: 1px solid #f4a100; padding: 6px 10px; border-radius: 6px; margin-top: 10px; }
</style>
</head>
<body>
<div id="root"></div>
<script type="text/babel" data-type="module">
import React, { useState } from "https://esm.sh/[email protected]";
import { createRoot } from "https://esm.sh/[email protected]/client";
function App() {
const [isLoggedIn, setIsLoggedIn] = useState(false);
const [unread, setUnread] = useState(3);
return (
<div>
<div className="bar">
<span>{isLoggedIn ? "Welcome back, Kavya" : "Hello, guest"}</span>
<button type="button" onClick={() => setIsLoggedIn(!isLoggedIn)}>
{isLoggedIn ? "Log out" : "Log in"}
</button>
</div>
{isLoggedIn && unread > 0 && (
<p className="alert">
You have {unread} unread messages.{" "}
<button type="button" onClick={() => setUnread(0)}>Mark all read</button>
</p>
)}
{!isLoggedIn && <p>Log in to see your course progress.</p>}
</div>
);
}
createRoot(document.getElementById("root")).render(<App />);
</script>
</body>
</html>Watch out: Do not put a number on the left of &&. If cart.length is 0, then {cart.length && <p>Items in cart</p>} evaluates to 0, and React prints a stray “0” on the page – it renders numbers, unlike false. Write a real comparison instead: {cart.length > 0 && …}.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>The 0 pitfall, side by side</title>
<script src="https://unpkg.com/@babel/[email protected]/babel.min.js"></script>
<style>
body { font-family: system-ui, sans-serif; padding: 16px; line-height: 1.5; }
.col { border: 1px dashed #bbb; padding: 8px 12px; margin: 8px 0; }
</style>
</head>
<body>
<div id="root"></div>
<script type="text/babel" data-type="module">
import React, { useState } from "https://esm.sh/[email protected]";
import { createRoot } from "https://esm.sh/[email protected]/client";
function App() {
const [cart, setCart] = useState([]);
return (
<div>
<button type="button" onClick={() => setCart([...cart, "Mysore pak"])}>Add sweet</button>{" "}
<button type="button" onClick={() => setCart([])}>Empty cart</button>
<div className="col">
<strong>Buggy:</strong> {cart.length && <span>{cart.length} items in cart</span>}
</div>
<div className="col">
<strong>Fixed:</strong> {cart.length > 0 ? <span>{cart.length} items in cart</span> : <span>Your cart is empty</span>}
</div>
</div>
);
}
createRoot(document.getElementById("root")).render(<App />);
</script>
</body>
</html>Rendering nothing with null
A component (see React Components) can decide to show nothing by returning null. This keeps the decision inside the component that knows the rules. For example, a SaleBanner can return null when no sale is running, and the parent can use <SaleBanner /> everywhere without checking first.
Storing JSX in a variable
As React conditional rendering logic grows, nested ternaries become hard to read. Instead, declare a variable with let, set it to different JSX in an if/else chain, and place the variable in your markup. For a fixed set of options, a lookup object is even neater.
// A variable assigned with if / else
function Delivery({ km }) {
let message;
if (km <= 5) {
message = <strong>Delivered in 30 minutes</strong>;
} else if (km <= 20) {
message = <span>Delivered today</span>;
} else {
message = <span>Delivered in 2–3 days</span>;
}
return <p>{message}</p>;
}
// A lookup object for fixed statuses
const statusLabels = {
pending: "Waiting for payment",
paid: "Payment received",
shipped: "On the way"
};
function Status({ status }) {
return <p>{statusLabels[status] ?? "Unknown status"}</p>;
}React conditional rendering versus hiding
Conditional rendering removes elements from the page completely. Any state inside a removed component is lost, and it starts fresh when it appears again. That is usually what you want. If you need to keep the state – for instance, text typed in a hidden tab – you can keep the component rendered and hide it with CSS or the hidden attribute instead.
| Pattern | Use it when |
|---|---|
if + early return | The whole output changes |
a ? <X /> : <Y /> | You show one of two things |
a && <X /> | You show something or nothing (keep a a true boolean) |
return null | A component should hide itself |
| Variable or lookup object | There are three or more cases |
Tip: For accessibility, when a message appears after an action – such as “Saved” or an error – put it inside an element that is already on the page with role="status" or aria-live="polite", so screen readers announce the change.
