Conditional Rendering 🔀

1. Introduction 👋

Conditional rendering lets your components display different UI depending on the current state, props, or other data. Because JSX is powered by plain JavaScript, React doesn't need special syntax for conditionals — it reuses familiar JavaScript constructs like if, ternaries, and logical operators.

Information

This tutorial builds on JSX and State. Reviewing those first will make the patterns here easier to follow.

2. What is Conditional Rendering? 🤔

Conditional rendering means choosing what to render based on a condition — showing a loading spinner while data fetches, displaying an error message, or rendering different content for logged-in versus logged-out users.

Code Snippet

function Greeting({ isLoggedIn }) {
  if (isLoggedIn) {
    return <h1>Welcome back! 👋</h1>;
  }
  return <h1>Please sign in.</h1>;
}

3. Why Conditional Rendering? 💡

  • Dynamic UI: Reflect the current application state accurately.
  • Better UX: Show loading, error, or empty states appropriately.
  • Access Control: Display content based on authentication or permissions.
  • Feature Management: Toggle experimental features on or off.

4. Rendering with if 🔹

A plain if statement can be used before the return statement to decide what JSX gets returned.

Code Snippet

function StatusBadge({ isOnline }) {
  if (isOnline) {
    return <span className="badge green">Online</span>;
  }
  return <span className="badge gray">Offline</span>;
}

Note

if statements cannot be used directly inside JSX curly braces — they are statements, not expressions.

5. Rendering with if...else 🔸

Code Snippet

function UserPanel({ user }) {
  if (user) {
    return <p>Logged in as {user.name}</p>;
  } else {
    return <p>Not logged in</p>;
  }
}

Tip

For simple two-way branches with early returns, plain if...else is often the most readable option, especially when each branch returns substantially different JSX.

6. Rendering with Ternary Operator ❓

The ternary operator (condition ? a : b) is an expression, making it ideal for inline conditionals directly within JSX.

Code Snippet

function StatusIcon({ isActive }) {
  return (
    <span>{isActive ? "đŸŸĸ Active" : "🔴 Inactive"}</span>
  );
}

Best Practice

Ternaries work well for simple, two-way choices. For more complex logic, extract it into a variable or helper function first.

7. Rendering with Logical && ✅

The logical AND operator renders its right-hand side only when the left-hand condition is true — useful for rendering something or nothing at all.

Code Snippet

function Notification({ hasUnread, count }) {
  return (
    <div>
      {hasUnread && <span className="badge">{count} new</span>}
    </div>
  );
}

Warning

Be careful with falsy numbers: {count && <Badge />} renders the number 0 on screen if count is 0, since 0 is falsy but still a renderable value.

8. Rendering with Logical || 🔁

The logical OR operator is useful for providing fallback content when a value is falsy, empty, or undefined.

Code Snippet

function UserName({ name }) {
  return <p>{name || "Anonymous User"}</p>;
}

Caution

|| treats 0, "", and false as falsy fallback triggers — use the nullish coalescing operator (??) instead if you only want to fall back on null or undefined.

9. Rendering with switch 🔀

For three or more mutually exclusive conditions, a switch statement (used before the return, or extracted into a helper function) is often clearer than chained ternaries.

Code Snippet

function StatusMessage({ status }) {
  switch (status) {
    case "loading":
      return <p>Loading... âŗ</p>;
    case "success":
      return <p>Success! ✅</p>;
    case "error":
      return <p>Something went wrong. ❌</p>;
    default:
      return <p>Idle</p>;
  }
}

10. Returning null đŸšĢ

A component can return null to render nothing at all — useful for conditionally hiding a component entirely.

Code Snippet

function WarningBanner({ show, message }) {
  if (!show) {
    return null;
  }
  return <div className="warning">{message}</div>;
}

Note

Returning null is completely valid in React — it simply results in no DOM output for that component instance.

11. Conditional Variables đŸ“Ļ

For complex conditions, assign the result to a variable before the return statement — this keeps the JSX itself clean and readable.

Code Snippet

function OrderStatus({ order }) {
  let statusText;

  if (order.isCancelled) {
    statusText = "Cancelled ❌";
  } else if (order.isDelivered) {
    statusText = "Delivered đŸ“Ļ";
  } else {
    statusText = "In Transit 🚚";
  }

  return <p>{statusText}</p>;
}

12. Multiple Conditions 🧩

Combine multiple boolean checks using logical operators (&&, ||) to express compound conditions.

Code Snippet

function SubmitButton({ isValid, isSubmitting }) {
  const canSubmit = isValid && !isSubmitting;

  return (
    <button disabled={!canSubmit}>
      {isSubmitting ? "Submitting..." : "Submit"}
    </button>
  );
}

Tip

Naming compound conditions in a clearly labeled variable (like canSubmit) makes intent obvious at a glance, compared to inlining the full boolean expression.

13. Nested Conditions đŸĒ†

While nesting ternaries or conditionals is possible, deeply nested logic quickly becomes hard to read. Prefer extracting nested conditions into separate variables, helper functions, or components.

Avoid: Deeply Nested Ternaries

// ❌ Hard to read
return isLoading ? <Spinner /> : error ? <ErrorMessage /> : data ? <Results data={data} /> : <EmptyState />;

Prefer: Early Returns

// ✅ Much clearer
if (isLoading) return <Spinner />;
if (error) return <ErrorMessage />;
if (!data) return <EmptyState />;
return <Results data={data} />;

14. Conditional Components 🧱

Sometimes the cleanest approach is choosing which component to render entirely, rather than conditionally rendering fragments of JSX.

Code Snippet

function Dashboard({ role }) {
  if (role === "admin") {
    return <AdminDashboard />;
  }
  return <UserDashboard />;
}

15. Conditional Props đŸŽ›ī¸

Props themselves can be computed conditionally before being passed down, keeping the JSX tag itself clean.

Code Snippet

function SubmitButton({ isLoading }) {
  const label = isLoading ? "Saving..." : "Save";

  return <Button label={label} disabled={isLoading} />;
}

16. Conditional Styling 🎨

Code Snippet

function AlertBox({ type }) {
  const style = {
    padding: "12px",
    backgroundColor: type === "error" ? "#fee2e2" : "#dcfce7",
    color: type === "error" ? "#991b1b" : "#166534",
  };

  return <div style={style}>Alert message</div>;
}

17. Conditional Classes đŸˇī¸

Code Snippet

function Tab({ isActive, label }) {
  return (
    <button className={isActive ? "tab tab-active" : "tab"}>
      {label}
    </button>
  );
}

Tip

For managing several conditional classes at once, a utility library like clsx keeps the logic readable: clsx("tab", isActive && "tab-active").

18. Conditional Attributes 🔧

HTML attributes like disabled, checked, and required can be conditionally applied using boolean expressions directly.

Code Snippet

function SubmitButton({ isFormValid }) {
  return <button disabled={!isFormValid}>Submit</button>;
}

function Checkbox({ isChecked }) {
  return <input type="checkbox" checked={isChecked} readOnly />;
}

19. Loading States âŗ

Code Snippet

function DataView({ isLoading, data }) {
  if (isLoading) {
    return <p>Loading data... âŗ</p>;
  }
  return <ul>{data.map((item) => <li key={item.id}>{item.name}</li>)}</ul>;
}

Best Practice

Always account for the loading state explicitly rather than assuming data is immediately available — this prevents errors from trying to render undefined data.

20. Empty States 📭

Code Snippet

function TodoList({ todos }) {
  if (todos.length === 0) {
    return <p>No todos yet. Add one to get started! ✨</p>;
  }

  return (
    <ul>
      {todos.map((todo) => <li key={todo.id}>{todo.text}</li>)}
    </ul>
  );
}

Tip

A well-designed empty state guides the user toward the next action, rather than leaving them staring at a blank screen.

21. Error States âš ī¸

Code Snippet

function UserProfile({ user, error }) {
  if (error) {
    return <p className="error">Failed to load profile: {error.message}</p>;
  }
  return <h1>{user.name}</h1>;
}

Warning

Always design a clear, actionable error state instead of letting a component crash or render blank content when something goes wrong.

22. Authentication-Based Rendering 🔐

Code Snippet

function Navbar({ isAuthenticated }) {
  return (
    <nav>
      {isAuthenticated ? (
        <button onClick={logout}>Log Out</button>
      ) : (
        <button onClick={login}>Log In</button>
      )}
    </nav>
  );
}

Note

For protecting entire routes (rather than just UI fragments), a dedicated ProtectedRoute wrapper component is a common pattern with routing libraries.

23. Role-Based Rendering đŸ‘Ĩ

Code Snippet

function AdminPanelLink({ role }) {
  if (role !== "admin") {
    return null;
  }
  return <a href="/admin">Admin Panel</a>;
}

Tip

Keep role-checking logic centralized (e.g., in a helper function or hook like useHasRole) rather than scattering raw string comparisons across many components.

24. Feature Flag Rendering 🚩

Code Snippet

function App({ featureFlags }) {
  return (
    <div>
      {featureFlags.newDashboard ? <NewDashboard /> : <LegacyDashboard />}
    </div>
  );
}

Information

Feature flags allow gradually rolling out new functionality, A/B testing UI variants, or quickly disabling a broken feature without a full deployment.

25. Permission-Based Rendering đŸ›Ąī¸

Code Snippet

function DeleteButton({ permissions }) {
  if (!permissions.includes("delete")) {
    return null;
  }
  return <button className="danger">Delete</button>;
}

Important

Client-side conditional rendering hides UI, but it is not a security boundary — always enforce permissions on the backend as well.

26. Best Practices 🌟

  1. Use early returns for readability instead of deeply nested ternaries.
  2. Extract complex conditions into named variables or helper functions.
  3. Always handle loading, empty, and error states explicitly.
  4. Use && carefully with numeric values to avoid rendering stray 0.
  5. Prefer ternaries for simple two-way branches, and switch or objects for many branches.

27. Common Mistakes đŸšĢ

  • Using {count && <Badge />} when count can be 0, accidentally rendering the number.
  • Deeply nesting ternary operators, making the JSX difficult to read and debug.
  • Forgetting to handle the loading state, causing errors when data is still undefined.
  • Using if statements directly inside JSX curly braces, which is invalid syntax.
  • Not returning null explicitly when a component should render nothing.

Danger

{0 && <Component />} silently renders a stray 0 on the page — convert to a boolean explicitly with {Boolean(count) && <Component />} or {count > 0 && <Component />}.

28. Performance Considerations ⚡

  • Conditionally rendering components unmounts them when hidden — this resets their internal state, unlike CSS-based visibility toggling.
  • For frequently toggled UI where state should persist (e.g., tab content), consider CSS display: none instead of removing the component from the tree.
  • Avoid recalculating expensive conditional logic on every render — memoize with useMemo if needed.

Note

Choosing between unmounting (via conditional rendering) and hiding (via CSS) depends on whether you want the hidden content's state and side effects to reset.

29. Frequently Asked Questions ❓

Question

Can I use an if statement directly inside JSX?

Answer

No — curly braces in JSX only accept expressions. Use a ternary, logical operator, or move the if logic above the return statement instead.

Question

What's the difference between returning null and rendering an empty string?

Answer

Both result in no visible output, but null is the conventional, explicit way to indicate "render nothing" in React and clearly communicates intent.

Question

Is conditional rendering enough to secure sensitive UI?

Answer

No — conditional rendering only affects what's displayed in the browser. Sensitive actions and data must always be protected on the backend as well.

30. Summary 📝

Conditional rendering in React relies entirely on standard JavaScript constructs — if statements, ternaries, and logical operators — applied to JSX. Mastering these patterns, along with handling loading, empty, and error states gracefully, is essential for building polished, production-ready interfaces.

Summary

With conditional rendering covered, a natural next step is exploring Rendering Lists and Keys in more depth, which often work hand-in-hand with conditional logic when displaying dynamic collections of data.
Conditional Rendering 🔀

1. Introduction 👋

Conditional rendering lets your components display different UI depending on the current state, props, or other data. Because JSX is powered by plain JavaScript, React doesn't need special syntax for conditionals — it reuses familiar JavaScript constructs like if, ternaries, and logical operators.

Information

This tutorial builds on JSX and State. Reviewing those first will make the patterns here easier to follow.

2. What is Conditional Rendering? 🤔

Conditional rendering means choosing what to render based on a condition — showing a loading spinner while data fetches, displaying an error message, or rendering different content for logged-in versus logged-out users.

Code Snippet

function Greeting({ isLoggedIn }) {
  if (isLoggedIn) {
    return <h1>Welcome back! 👋</h1>;
  }
  return <h1>Please sign in.</h1>;
}

3. Why Conditional Rendering? 💡

  • Dynamic UI: Reflect the current application state accurately.
  • Better UX: Show loading, error, or empty states appropriately.
  • Access Control: Display content based on authentication or permissions.
  • Feature Management: Toggle experimental features on or off.

4. Rendering with if 🔹

A plain if statement can be used before the return statement to decide what JSX gets returned.

Code Snippet

function StatusBadge({ isOnline }) {
  if (isOnline) {
    return <span className="badge green">Online</span>;
  }
  return <span className="badge gray">Offline</span>;
}

Note

if statements cannot be used directly inside JSX curly braces — they are statements, not expressions.

5. Rendering with if...else 🔸

Code Snippet

function UserPanel({ user }) {
  if (user) {
    return <p>Logged in as {user.name}</p>;
  } else {
    return <p>Not logged in</p>;
  }
}

Tip

For simple two-way branches with early returns, plain if...else is often the most readable option, especially when each branch returns substantially different JSX.

6. Rendering with Ternary Operator ❓

The ternary operator (condition ? a : b) is an expression, making it ideal for inline conditionals directly within JSX.

Code Snippet

function StatusIcon({ isActive }) {
  return (
    <span>{isActive ? "đŸŸĸ Active" : "🔴 Inactive"}</span>
  );
}

Best Practice

Ternaries work well for simple, two-way choices. For more complex logic, extract it into a variable or helper function first.

7. Rendering with Logical && ✅

The logical AND operator renders its right-hand side only when the left-hand condition is true — useful for rendering something or nothing at all.

Code Snippet

function Notification({ hasUnread, count }) {
  return (
    <div>
      {hasUnread && <span className="badge">{count} new</span>}
    </div>
  );
}

Warning

Be careful with falsy numbers: {count && <Badge />} renders the number 0 on screen if count is 0, since 0 is falsy but still a renderable value.

8. Rendering with Logical || 🔁

The logical OR operator is useful for providing fallback content when a value is falsy, empty, or undefined.

Code Snippet

function UserName({ name }) {
  return <p>{name || "Anonymous User"}</p>;
}

Caution

|| treats 0, "", and false as falsy fallback triggers — use the nullish coalescing operator (??) instead if you only want to fall back on null or undefined.

9. Rendering with switch 🔀

For three or more mutually exclusive conditions, a switch statement (used before the return, or extracted into a helper function) is often clearer than chained ternaries.

Code Snippet

function StatusMessage({ status }) {
  switch (status) {
    case "loading":
      return <p>Loading... âŗ</p>;
    case "success":
      return <p>Success! ✅</p>;
    case "error":
      return <p>Something went wrong. ❌</p>;
    default:
      return <p>Idle</p>;
  }
}

10. Returning null đŸšĢ

A component can return null to render nothing at all — useful for conditionally hiding a component entirely.

Code Snippet

function WarningBanner({ show, message }) {
  if (!show) {
    return null;
  }
  return <div className="warning">{message}</div>;
}

Note

Returning null is completely valid in React — it simply results in no DOM output for that component instance.

11. Conditional Variables đŸ“Ļ

For complex conditions, assign the result to a variable before the return statement — this keeps the JSX itself clean and readable.

Code Snippet

function OrderStatus({ order }) {
  let statusText;

  if (order.isCancelled) {
    statusText = "Cancelled ❌";
  } else if (order.isDelivered) {
    statusText = "Delivered đŸ“Ļ";
  } else {
    statusText = "In Transit 🚚";
  }

  return <p>{statusText}</p>;
}

12. Multiple Conditions 🧩

Combine multiple boolean checks using logical operators (&&, ||) to express compound conditions.

Code Snippet

function SubmitButton({ isValid, isSubmitting }) {
  const canSubmit = isValid && !isSubmitting;

  return (
    <button disabled={!canSubmit}>
      {isSubmitting ? "Submitting..." : "Submit"}
    </button>
  );
}

Tip

Naming compound conditions in a clearly labeled variable (like canSubmit) makes intent obvious at a glance, compared to inlining the full boolean expression.

13. Nested Conditions đŸĒ†

While nesting ternaries or conditionals is possible, deeply nested logic quickly becomes hard to read. Prefer extracting nested conditions into separate variables, helper functions, or components.

Avoid: Deeply Nested Ternaries

// ❌ Hard to read
return isLoading ? <Spinner /> : error ? <ErrorMessage /> : data ? <Results data={data} /> : <EmptyState />;

Prefer: Early Returns

// ✅ Much clearer
if (isLoading) return <Spinner />;
if (error) return <ErrorMessage />;
if (!data) return <EmptyState />;
return <Results data={data} />;

14. Conditional Components 🧱

Sometimes the cleanest approach is choosing which component to render entirely, rather than conditionally rendering fragments of JSX.

Code Snippet

function Dashboard({ role }) {
  if (role === "admin") {
    return <AdminDashboard />;
  }
  return <UserDashboard />;
}

15. Conditional Props đŸŽ›ī¸

Props themselves can be computed conditionally before being passed down, keeping the JSX tag itself clean.

Code Snippet

function SubmitButton({ isLoading }) {
  const label = isLoading ? "Saving..." : "Save";

  return <Button label={label} disabled={isLoading} />;
}

16. Conditional Styling 🎨

Code Snippet

function AlertBox({ type }) {
  const style = {
    padding: "12px",
    backgroundColor: type === "error" ? "#fee2e2" : "#dcfce7",
    color: type === "error" ? "#991b1b" : "#166534",
  };

  return <div style={style}>Alert message</div>;
}

17. Conditional Classes đŸˇī¸

Code Snippet

function Tab({ isActive, label }) {
  return (
    <button className={isActive ? "tab tab-active" : "tab"}>
      {label}
    </button>
  );
}

Tip

For managing several conditional classes at once, a utility library like clsx keeps the logic readable: clsx("tab", isActive && "tab-active").

18. Conditional Attributes 🔧

HTML attributes like disabled, checked, and required can be conditionally applied using boolean expressions directly.

Code Snippet

function SubmitButton({ isFormValid }) {
  return <button disabled={!isFormValid}>Submit</button>;
}

function Checkbox({ isChecked }) {
  return <input type="checkbox" checked={isChecked} readOnly />;
}

19. Loading States âŗ

Code Snippet

function DataView({ isLoading, data }) {
  if (isLoading) {
    return <p>Loading data... âŗ</p>;
  }
  return <ul>{data.map((item) => <li key={item.id}>{item.name}</li>)}</ul>;
}

Best Practice

Always account for the loading state explicitly rather than assuming data is immediately available — this prevents errors from trying to render undefined data.

20. Empty States 📭

Code Snippet

function TodoList({ todos }) {
  if (todos.length === 0) {
    return <p>No todos yet. Add one to get started! ✨</p>;
  }

  return (
    <ul>
      {todos.map((todo) => <li key={todo.id}>{todo.text}</li>)}
    </ul>
  );
}

Tip

A well-designed empty state guides the user toward the next action, rather than leaving them staring at a blank screen.

21. Error States âš ī¸

Code Snippet

function UserProfile({ user, error }) {
  if (error) {
    return <p className="error">Failed to load profile: {error.message}</p>;
  }
  return <h1>{user.name}</h1>;
}

Warning

Always design a clear, actionable error state instead of letting a component crash or render blank content when something goes wrong.

22. Authentication-Based Rendering 🔐

Code Snippet

function Navbar({ isAuthenticated }) {
  return (
    <nav>
      {isAuthenticated ? (
        <button onClick={logout}>Log Out</button>
      ) : (
        <button onClick={login}>Log In</button>
      )}
    </nav>
  );
}

Note

For protecting entire routes (rather than just UI fragments), a dedicated ProtectedRoute wrapper component is a common pattern with routing libraries.

23. Role-Based Rendering đŸ‘Ĩ

Code Snippet

function AdminPanelLink({ role }) {
  if (role !== "admin") {
    return null;
  }
  return <a href="/admin">Admin Panel</a>;
}

Tip

Keep role-checking logic centralized (e.g., in a helper function or hook like useHasRole) rather than scattering raw string comparisons across many components.

24. Feature Flag Rendering 🚩

Code Snippet

function App({ featureFlags }) {
  return (
    <div>
      {featureFlags.newDashboard ? <NewDashboard /> : <LegacyDashboard />}
    </div>
  );
}

Information

Feature flags allow gradually rolling out new functionality, A/B testing UI variants, or quickly disabling a broken feature without a full deployment.

25. Permission-Based Rendering đŸ›Ąī¸

Code Snippet

function DeleteButton({ permissions }) {
  if (!permissions.includes("delete")) {
    return null;
  }
  return <button className="danger">Delete</button>;
}

Important

Client-side conditional rendering hides UI, but it is not a security boundary — always enforce permissions on the backend as well.

26. Best Practices 🌟

  1. Use early returns for readability instead of deeply nested ternaries.
  2. Extract complex conditions into named variables or helper functions.
  3. Always handle loading, empty, and error states explicitly.
  4. Use && carefully with numeric values to avoid rendering stray 0.
  5. Prefer ternaries for simple two-way branches, and switch or objects for many branches.

27. Common Mistakes đŸšĢ

  • Using {count && <Badge />} when count can be 0, accidentally rendering the number.
  • Deeply nesting ternary operators, making the JSX difficult to read and debug.
  • Forgetting to handle the loading state, causing errors when data is still undefined.
  • Using if statements directly inside JSX curly braces, which is invalid syntax.
  • Not returning null explicitly when a component should render nothing.

Danger

{0 && <Component />} silently renders a stray 0 on the page — convert to a boolean explicitly with {Boolean(count) && <Component />} or {count > 0 && <Component />}.

28. Performance Considerations ⚡

  • Conditionally rendering components unmounts them when hidden — this resets their internal state, unlike CSS-based visibility toggling.
  • For frequently toggled UI where state should persist (e.g., tab content), consider CSS display: none instead of removing the component from the tree.
  • Avoid recalculating expensive conditional logic on every render — memoize with useMemo if needed.

Note

Choosing between unmounting (via conditional rendering) and hiding (via CSS) depends on whether you want the hidden content's state and side effects to reset.

29. Frequently Asked Questions ❓

Question

Can I use an if statement directly inside JSX?

Answer

No — curly braces in JSX only accept expressions. Use a ternary, logical operator, or move the if logic above the return statement instead.

Question

What's the difference between returning null and rendering an empty string?

Answer

Both result in no visible output, but null is the conventional, explicit way to indicate "render nothing" in React and clearly communicates intent.

Question

Is conditional rendering enough to secure sensitive UI?

Answer

No — conditional rendering only affects what's displayed in the browser. Sensitive actions and data must always be protected on the backend as well.

30. Summary 📝

Conditional rendering in React relies entirely on standard JavaScript constructs — if statements, ternaries, and logical operators — applied to JSX. Mastering these patterns, along with handling loading, empty, and error states gracefully, is essential for building polished, production-ready interfaces.

Summary

With conditional rendering covered, a natural next step is exploring Rendering Lists and Keys in more depth, which often work hand-in-hand with conditional logic when displaying dynamic collections of data.