Topic 3.1
Conditional Rendering
In one line
Conditional rendering means a component shows different UI for different data, using plain JavaScript: an if with an early return, the ternary ? :, &&, returning null, conditional props and classes, and picking a component from a variable or an object map.
Think of it like this
A shop door sign. The shopkeeper doesn't paint a new door every evening. The sign has two sides, OPEN and CLOSED, and the time of day decides which side faces the street. A React component works the same way: it has all the possible pictures inside it, and the data decides which one is shown right now.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- Conditional rendering
- Showing different UI depending on data, using
if,? :or&&. - Condition
- A value or test that is true or false, such as
isLoggedInorcount > 0. - Early return (guard clause)
- Checking a special case at the top of a function and returning right away, so the rest only handles the normal case.
- Ternary operator
condition ? a : b: givesawhen the condition is true,bwhen it is false.- Logical AND (&&)
a && bgivesaifais falsy, otherwiseb. In JSX it means "show this or nothing".- Falsy
- A value JavaScript treats as false:
false,0,"",null,undefinedandNaN. - null
- A value meaning "nothing". A component that returns
nullshows nothing. - Object map (lookup table)
- An object that maps names to values, like
{ grid: GridView, list: ListView }, used instead of a long if/else chain.
Step by step
01Start with the special case: an early return
An inbox makes no sense without a signed-in user. Handle that first and leave. Everything after the if can assume user exists.
function Inbox({ user }) {
if (!user) {
return <p>Please sign in to see your messages.</p>;
}
return <h2>Hello, {user.name}!</h2>;
}02Pick one of two inside JSX: a ternary
When only a small part changes, put the choice right where it shows. A ternary always produces one of its two values.
<h2>{user.isAdmin ? "Admin inbox" : "Your inbox"}</h2>03Show or hide one thing: && with a real boolean
To show something only sometimes, use &&. Make the left side a true comparison (> 0), never a bare number, or an empty count will put a 0 on the page.
{unread > 0 && <p className="badge">{unread} unread</p>}04How React treats each value
Whatever a condition produces ends up as a child in JSX. This table is what decides what you see.
05Make attributes and classes conditional
Attributes take expressions too. Use a ternary, and undefined when the attribute should not exist at all.
<button
disabled={!changed}
className={changed ? "save ready" : "save"}
title={changed ? undefined : "Nothing to save yet"}
>
Save
</button>06Many choices: a variable or an object map
With three or more cases, decide before the return. An object map from names to components is the neatest: the JSX stays one line, and adding a case is one line of data.
const VIEWS = { grid: GridView, list: ListView, count: CountView };
function Gallery({ mode, photos }) {
const View = VIEWS[mode]; // capital V: JSX needs it
return <View photos={photos} />;
}Try it yourself
- 1
Make yourself an admin
In "Four ways to choose", change
isAdmin: falsetoisAdmin: true. Predict which heading appears after you sign in, then check. - 2
See the gotcha disappear
In "The 0 && gotcha", change
Cartto use!!items.length &&. Predict the console line first. (Cart shows: "":!!0isfalse, and React shows nothing forfalse.) - 3
Add a view with one line
In the gallery, write a
ShoutViewthat renders the photo names in capitals, and addshout: ShoutViewtoVIEWS. A new button appears by itself, because the buttons are made fromObject.keys(VIEWS). - 4
Hide in the parent instead
In the first example, remove the
iffromBannerand write{signedIn && <Banner />}inAppinstead. The page looks the same. What is the difference? (NowBannerisn't called at all while hidden.)
Code & diagrams
Click **Sign in**, then **New message** a few times. The early return, the ternary, `&&` and `return null` each decide one part of the screen.
Preview
Console
Nothing logged yet.The first box shows a lonely **0**. The fix is a real comparison on the left of `&&`.
Preview
Console
Nothing logged yet.Expected console output
Cart shows: "0"
FixedCart shows: ""The buttons get a conditional class and `aria-pressed`, and `VIEWS[mode]` picks which component renders. Try adding a fourth view.
Preview
Console
Nothing logged yet.Break it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Use && for a normal attribute
In the gallery, replace the button's className with className={name === mode && "tab active"}.
Break #2
Name the chosen component with a small letter
In the gallery, rename const View to const view and render <view photos={photos} />.
Myth vs fact
Myth
React needs special syntax like v-if for conditions.
Fact
React uses plain JavaScript: if, ? :, && and variables. There is nothing extra to learn, only JavaScript's rules about which values are falsy.
Myth
{count && <Thing />} is always safe.
Fact
When count is 0, the expression gives 0, and React renders numbers. Use count > 0 &&.
Myth
A component that returns null doesn't run.
Fact
It runs every time its parent renders: its hooks run and its state is kept. It just adds nothing to the page. To skip running it, don't render it from the parent.
Myth
Hiding something with a condition is the same as hiding it with CSS.
Fact
A condition removes the elements from the DOM and, when the component itself is removed, throws away its state. CSS (display: none or the hidden attribute) keeps the DOM and state. React 19.2's <Activity mode="hidden"> hides while keeping state (Topic 16.10).
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
When a ternary swaps between two different component types at the same spot (
{isAdmin ? <AdminForm /> : <UserForm />}), React unmounts one and mounts the other, so all state and DOM inside are lost. Two of the same type at the same spot ({isAdmin ? <Form role="admin" /> : <Form role="user" />}) keep their state, because React matches by type and position. This is the reconciliation rule you'll study in Topics 6.4 and 16.2. - ▸
{cond && <A />}leaves a hole at that position when false, so siblings after it keep their positions and their state. That's why React handles conditional children without keys: afalseornullchild still takes up its slot in the children array. - ▸
Never call a hook inside a condition (
if (open) { useState(...) }). Hooks must run in the same order every render. Put the condition inside the JSX or inside the hook instead (Topic 4.3). - ▸
An object map of components should be created outside the component (module level). Creating
const VIEWS = { ... }with components defined inside the render would make new component types every render and reset their state each time (Topic 2.1).
Remember this
- 1
Conditional rendering means returning different JSX depending on a condition (a value that is true or false, such as
isLoggedInoritems.length > 0). React has no specialv-ifor*ngIfsyntax. You use the JavaScript you already know:if, the ternary operator (condition ? a : b) and the logical AND operator (&&). The only rule to remember is the one from Topic 1.3: inside JSX curly braces you may write an expression (something that produces a value), not a statement (an instruction likeiforfor). Soifgoes *before* thereturn, and? :and&&go *inside* the JSX. - 2
If with an early return. When a whole component looks completely different in some case, check that case first and
returnstraight away:if (!user) return <p>Please sign in</p>;. The code below it only runs for the normal case, so it can safely useuser.name. This pattern is called a guard clause. It keeps the main JSX flat and readable instead of wrapping everything in one big condition. - 3
Ternary: this or that.
{isAdmin ? <AdminPanel /> : <UserPanel />}shows exactly one of two things. Use it for small choices inside JSX: a label ({done ? "Done" : "To do"}), an icon, one of two components. Nested ternaries (a ? x : b ? y : z) work but become hard to read quickly; past two levels, move the choice into a variable or an object map (below). - 4
&&: something or nothing.
{unread > 0 && <Badge />}shows the badge when the left side is true and nothing otherwise. It works because JavaScript'sa && bgives backawhenais falsy (a value that counts as false:false,0,"",null,undefined,NaN), and gives backbotherwise. React renders nothing forfalse,null,undefinedandtrue, but it does render numbers, including0. So{items.length && <List />}shows a stray 0 on the page when the array is empty. Always make the left side a real boolean:items.length > 0 &&,!!items.length &&, orBoolean(items.length) &&. - 5
Returning null. A component may return
nullto render nothing at all:if (!show) return null;. The component still runs: React calls it, its hooks run and its state is kept. It only puts nothing on the screen. Since React 18 returningundefinedis also allowed (React 17 threw an error), butnullsays "nothing, on purpose" more clearly. Whether to hide something inside the child (return null) or in the parent ({show && <Child />}) is a design choice: the parent version doesn't run the child at all, and its state is thrown away when it is hidden. - 6
Conditional props and classes. Conditions work for attributes too:
disabled={!agreed},aria-expanded={open},className={active ? "tab active" : "tab"}. To leave an attribute out completely, passundefined(title={saved ? "Saved" : undefined}); React then doesn't write it to the DOM. Don't use&&for non-boolean attributes:title={saved && "Saved"}passesfalsewhen not saved, and React warns about it. For several optional classes, build a list and join it:["tab", active && "active", error && "error"].filter(Boolean).join(" "). The tinyclsxpackage does exactly this (Topic 1.5 and Phase 11). - 7
Choosing a component with a variable or an object map. For more than two choices, decide before the JSX. Either assign JSX to a variable (
let content; if (status === "loading") content = <Spinner />; else if (...) ...), or keep a lookup object from names to components:const VIEWS = { grid: GridView, list: ListView }and thenconst View = VIEWS[mode]; return <View photos={photos} />;. The variable must start with a capital letter (View, notview), because JSX treats lowercase names as HTML tags. An object map turns a longif/elsechain into data: adding a view is one new line. Note that switching to a different component type throws away the old one's state (Topic 6.4).
Explain it without notes
Why can't you write if directly inside JSX curly braces, and what do you use instead?
Explain the 0 && bug and two ways to fix it.
What happens when a component returns null? How is that different from not rendering it in the parent?
How do you choose between many components without a long chain of ternaries?
How do you make an attribute or a class conditional, and how do you leave an attribute out completely?
Practice
Write a Status component that takes an online boolean and shows a green Online or a grey Offline label, using a ternary for both the text and the class.
Write a Notifications component that takes count and shows You have N new notifications only when count is above zero, and null otherwise. Make sure count = 0 shows nothing at all.
Write a Weather component that takes kind ("sun", "rain" or "snow") and renders <Sun />, <Rain /> or <Snow /> using an object map, with a fallback <p>Unknown weather</p> for any other value.
Trade-offs
- ↔
Early returns keep the main JSX simple but spread the output over several
returns; for a component with many states, an object map or a smallswitchkeeps every case visible in one place. - ↔
Removing UI with a condition frees its DOM and state (good for memory, bad if the user expects their half-typed text to survive). Hiding with CSS or
<Activity>keeps them, at the cost of keeping them in memory.
Done when you can
I can use an early return for a special case.
I can choose between two things with a ternary and show-or-nothing with
&&.I can explain and avoid the
0 &&bug.I know a component may return
nulland that it still runs.I can make attributes and classes conditional and omit an attribute with
undefined.I can choose a component from an object map with a capitalised variable.