Conditional rendering
if and an early return
import { useState } from "react";
function Greeting({ user }) {
if (!user) {
return <p>Please sign in.</p>;
}
return <p>Welcome back, {user}!</p>;
}
function App() {
const [user, setUser] = useState(null);
return (
<div>
<Greeting user={user} />
<button onClick={() => setUser(user ? null : "Asha")}>{user ? "Sign out" : "Sign in"}</button>
</div>
);
}A component is a function, so ordinary if statements work. Returning early for the special case keeps the main markup unindented.
The ternary operator in JSX
import { useState } from "react";
function App() {
const [online, setOnline] = useState(true);
return (
<div>
<p>{online ? "🟢 Online" : "⚪ Offline"}</p>
<button onClick={() => setOnline(!online)}>Toggle</button>
</div>
);
}if can't go inside JSX's braces, because only expressions can. condition ? a : b is an expression, so it can.
&& for "this or nothing"
import { useState } from "react";
function App() {
const [unread, setUnread] = useState(3);
return (
<div>
<h3>
Inbox{" "}
{unread > 0 && (
<span style={{ background: "#e0446a", color: "white", borderRadius: 999, padding: "2px 8px", fontSize: 13 }}>
{unread}
</span>
)}
</h3>
<button onClick={() => setUnread(unread + 1)}>New message</button>{" "}
<button onClick={() => setUnread(0)}>Mark all read</button>
</div>
);
}condition && <Thing /> renders the thing when the condition is true, and nothing when it's false: React ignores false, null and undefined.
The 0 && trap
function App() {
const items = [];
return (
<div>
<p>Wrong: {items.length && <span>{items.length} items</span>}</p>
<p>Right: {items.length > 0 && <span>{items.length} items</span>}</p>
</div>
);
}With an empty list, items.length && ... evaluates to 0, and React does render the number 0, so a stray "0" appears on the page. Make the left side a real true-or-false value, such as items.length > 0.
Several states: loading, error, success
import { useState } from "react";
function Status({ state }) {
switch (state) {
case "loading":
return <p>⏳ Saving...</p>;
case "error":
return <p style={{ color: "crimson" }}>Something went wrong. Try again.</p>;
case "success":
return <p style={{ color: "green" }}>✔ Saved!</p>;
default:
return null;
}
}
function App() {
const [state, setState] = useState("idle");
return (
<div>
{["idle", "loading", "error", "success"].map((s) => (
<button key={s} onClick={() => setState(s)} style={{ marginRight: 6 }}>{s}</button>
))}
<Status state={state} />
</div>
);
}A component that returns null renders nothing at all. When there are more than two cases, a switch in its own small component is easier to read than nested ternaries.
Conditional styles
import { useState } from "react";
function Task({ label }) {
const [done, setDone] = useState(false);
return (
<li
onClick={() => setDone(!done)}
style={{ cursor: "pointer", textDecoration: done ? "line-through" : "none", color: done ? "#999" : "inherit" }}
>
{done ? "✔ " : "○ "}{label}
</li>
);
}
function App() {
return (
<ul style={{ listStyle: "none", padding: 0 }}>
<Task label="Read the lesson" />
<Task label="Run the examples" />
<Task label="Take the quiz" />
</ul>
);
}Click a task to tick it off. The same ternary pattern works for className: className={done ? "task done" : "task"}.