Conditional UI in the First App
The first app receives fixed props, and those props can describe different interface states.
<LessonCard complete={false} />
<LessonCard complete />The component decides which React output represents each value. It does not find a rendered node and toggle it afterward. Each component call calculates its result for the current props.
Conditional UI is therefore ordinary JavaScript selection applied to React nodes. The selection can choose an entire return value, one value inside JSX, or an optional part of a larger tree.
Begin with the UI States
Before choosing syntax, list the states the component supports.
LessonCard has three statuses.
not-started
reading
completeEach status needs an intentional visible result.
| Status | Label | Available action |
|---|---|---|
not-started | Not started | Start lesson |
reading | In progress | Continue lesson |
complete | Complete | Review lesson |
The table is part of component design. It prevents a branch from displaying a label from one state and an action from another.
Model the supported states before compressing them into JSX syntax.
A boolean can represent two states. A named string union is clearer when there are three or more mutually exclusive states. JavaScript accepts the strings at runtime, and TypeScript can restrict the supported set statically.
Use an Early Return for a Whole Result
A draft lesson should produce no card for readers.
function LessonCard({ lesson }) {
if (!lesson.published) {
return null;
}
return <article>{lesson.title}</article>;
}The early return handles a complete component result. No host node is produced for an unpublished lesson.
The remaining code can proceed knowing lesson.published is true. This removes the need to indent the whole card inside an else block.
function LessonCard({ lesson }) {
if (!lesson.published) {
return null;
} else {
return <article>{lesson.title}</article>;
}
}The else is unnecessary after the first branch returns.
Use early returns for states that replace the whole component result, including missing required data, unavailable content, and full loading or error screens.
Returning null keeps the component in the React tree but produces no host output for that render.
Use a Conditional Expression for One of Two Values
Choose a label inside a paragraph.
function LessonStatus({ complete }) {
return (
<p>{complete ? "Complete" : "Not started"}</p>
);
}The condition appears before ?. The expression after ? is selected when the condition is truthy. The expression after the second separator is selected otherwise.
Both branches produce strings in this example. They can also produce elements.
const status = complete
? <strong>Complete</strong>
: <span>Not started</span>;Render the selected value.
return <p>Status {status}</p>;Use a variable when putting the full expression inside JSX makes the returned structure hard to scan.
Do Not Build Nested Conditional Chains
Three states can fit into nested conditional expressions.
const label = complete
? "Complete"
: started
? "In progress"
: "Not started";The reader has to pair each branch with its condition through indentation. Add a fourth state and the expression becomes a poor state model.
Use a function or object keyed by the status.
const labels = {
"not-started": "Not started",
reading: "In progress",
complete: "Complete",
};Read the current label.
const label = labels[status];When each state needs several UI decisions, a switch can keep one state together.
function getActionLabel(status) {
switch (status) {
case "not-started": return "Start lesson";
case "reading": return "Continue lesson";
case "complete": return "Review lesson";
default: throw new Error(`Unknown lesson status ${status}`);
}
}The default branch exposes invalid runtime data instead of quietly displaying the wrong action.
Use Logical AND for an Optional Piece
Display a badge only when the lesson is new.
function LessonTitle({ title, isNew }) {
return (
<h2>
{title}
{isNew && <span>New</span>}
</h2>
);
}When isNew is true, the expression returns the span element. When it is false, the expression returns false, which React renders as no text or host node.
This form fits an optional piece whose absent result is empty. Use a conditional expression when both states need visible output.
{complete ? <CompleteBadge /> : <ReadingBadge />}Use logical AND for present-or-empty output. Use a conditional expression for one-of-two output.
Guard Numeric Conditions Explicitly
Logical AND returns one of its operands. It does not convert every result to a boolean.
function RemainingCount({ count }) {
return <p>{count && <span>{count} remaining</span>}</p>;
}When count is 0, the expression returns the number 0. React renders that number, so a visible zero appears where empty output was intended.
Compare explicitly.
function RemainingCount({ count }) {
return (
<p>{count > 0 && <span>{count} remaining</span>}</p>
);
}The comparison produces a boolean. False becomes empty React output.
Strings have a related case. An empty string is falsy and also renders as empty text. Decide whether an empty label means missing data or a valid blank value before using it as a condition.
Distinguish Not Rendered from Visually Hidden
This condition omits the details element from the React output.
{showDetails && <LessonDetails />}When false, LessonDetails is absent from that returned tree. Its host nodes are not in the DOM.
CSS can leave a node in the DOM and hide its presentation.
<div hidden={!showDetails}>
<LessonDetails />
</div>The hidden attribute makes the content not relevant for normal rendering while the DOM nodes remain. Other CSS techniques have different accessibility behavior.
Choose based on lifecycle and document requirements. Optional content that has no reason to exist can be omitted. Content whose DOM state must be retained may stay rendered with an appropriate hidden mechanism.
Removing a component also ends that rendered component instance. Hiding an existing DOM node and removing the component are therefore different operations.
Keep Related Decisions on One Status
Avoid separate booleans that can contradict one another.
<LessonCard
started
complete
locked
/>The element describes a lesson that is started, complete, and locked. The component needs precedence rules for combinations that may never be intended.
Use one status when the options are mutually exclusive.
<LessonCard status="complete" />Separate booleans remain appropriate for independent facts. A completed lesson can also be featured, so those values do not need one combined status.
<LessonCard status="complete" featured />The data model determines which combinations are valid. JSX syntax cannot repair contradictory props after they are supplied.
Several booleans used as one state can produce combinations with no defined UI. Use one discriminating value for mutually exclusive states.
Cover Loading, Error, Empty, and Content Separately
A data-driven list can have four broad results.
loading
error
loaded with no lessons
loaded with lessonsDo not use one empty array to represent all four. An empty list after successful loading is different from data that has not arrived.
A component can use early returns to keep those results complete.
if (loading) return <p>Loading lessons</p>;
if (error) return <p>Lessons could not be loaded</p>;
if (lessons.length === 0) return <p>No lessons available</p>;After those branches, the remaining path has a populated list and can render it.
Keep Hooks Before Conditional Returns that Can Change Call Order
A condition must not change whether a Hook is called. React requires Hooks to run in the same order on every render of a component.
function LessonDetails({ available }) {
if (!available) return null;
const [open, setOpen] = useState(false);
// ...
}When available changes, the component can call a Hook in one render and skip it in another. That violates the required stable Hook order.
Call Hooks unconditionally at the top level, then branch.
function LessonDetails({ available }) {
const [open, setOpen] = useState(false);
if (!available) return null;
// ...
}Assemble the First Conditional Card
Define the state-specific labels outside the component.
const statusLabels = {
"not-started": "Not started",
reading: "In progress",
complete: "Complete",
};Render one complete card result.
function LessonCard({ lesson }) {
if (!lesson.published) return null;
const label = statusLabels[lesson.status];
return (
<article>
<h2>{lesson.title}</h2>
<p>{label}</p>
{lesson.isNew && <span>New</span>}
</article>
);
}The early return hides unpublished content. The label map selects one of three labels. Logical AND includes the optional badge. Each syntax form has one clear role.
Validate unexpected status values at the data entry point or throw a descriptive development error before rendering an undefined label.
Check the Component Model
The application uses the complete component input and output model.
an uppercase binding supplies a component type
an imported function keeps that type available across files
one component call returns one React node tree
the parent supplies read-only props
nested content arrives through children
JavaScript conditions select the current React outputSource Notes
- React documentation, Conditional Rendering
- React documentation, Rules of Hooks
- MDN, Conditional operator
- MDN, Logical AND