Skip to main content

Command Palette

Search for a command to run...

Conditional Rendering in React Apps

Simplifying React with &&, Ternary, and Early Return Techniques

Updated
6 min read
Conditional Rendering in React Apps

In simple term, it is used to render or show UI elements / Components based on condition.

Conditional rendering in React is the process of displaying different UI elements or components based on specific conditions, such as the application's state, user roles, or input data. It uses standard JavaScript logic and syntax to dynamically control what is rendered to the user.

There are many ways to implement conditional rendering, each one has specific use case. Developers can use several JavaScript techniques to implement conditional rendering in React components:

TechniqueDescriptionUse Case
if statementsUse standard if or if-else blocks outside of the return statement in a functional component to decide which JSX to return. This is ideal for early returns or complex logic.Displaying a login form or a user dashboard depending on an isLoggedIn state.
Ternary Operator (? :)A concise inline if-else statement within JSX. It is used when you need to render one of two possible outcomes.Toggling button text between "Login" and "Logout" based on a boolean isLoggedIn variable.
Logical && OperatorRenders the element on the right side if the condition on the left side is true; otherwise, it renders nothing (null). This is known as short-circuit evaluation.Showing a notification message only if there are unread messages (messageCount > 0 && <Notification />).
Element VariablesAssign JSX to a variable within the component logic and then include the variable in the main return statement.A cleaner way to handle slightly more complex conditional logic while keeping the return block simple.
switch statementsUseful for handling multiple possible conditions in a readable manner. Like if-else statements, they are typically used outside the main return to define a variable holding the output.Rendering different components based on a userRole (e.g., 'viewer', 'editor', 'admin').

Detailed Example of each Technique

These examples are based on production-grade standards.

1. Logical AND (&&) Operator

Use case: Show optional UI elements that depend on a single condition

function NotificationBadge({ unreadCount }) {
  return (
    <div className="nav-item">
      <BellIcon />
      {unreadCount > 0 && (
        <span className="badge">{unreadCount}</span>
      )}
    </div>
  );
}

Why this pattern: Perfect when you want to render something or nothing. If unreadCount is 0, the badge doesn't appear at all.

Production tip: Be careful with falsy values. {count && <Badge />} will render "0" on screen if count is 0. Use {count > 0 && <Badge />} instead. And you don’t want to show that.


2. Ternary Operator (? :)

Use case: Toggle between two different UI states

function SubscribeButton({ isSubscribed, onToggle }) {
  return (
    <button 
      onClick={onToggle}
      className={isSubscribed ? "btn-secondary" : "btn-primary"}
    >
      {isSubscribed ? "Unsubscribe" : "Subscribe"}
    </button>
  );
}

Why this pattern: When you need to show one thing or another (not nothing). Both states always render something.

Production tip: Keep ternaries simple. If the JSX gets complex, extract to variables or use early returns instead.


3. Early Return

Use case: Handle loading/error states before rendering main content. When you want to show skeleton loader while fetching and processing data.

function UserProfile({ userId }) {
  const { data, isLoading, error } = useUser(userId);

  if (isLoading) {
    return <Spinner />;
  }

  if (error) {
    return <ErrorMessage error={error} />;
  }

  if (!data) {
    return <EmptyState message="User not found" />;
  }

  // Main rendering logic - clean and focused
  return (
    <div className="profile">
      <Avatar src={data.avatar} />
      <h1>{data.name}</h1>
      <Bio text={data.bio} />
    </div>
  );
}

Why this pattern: Keeps your main component logic clean by handling edge cases first. No deeply nested ternaries or complex conditionals.

Production tip: This is the most maintainable pattern for components with multiple states (loading, error, empty, success). Always handle edge cases at the top.


4. if Statements

Use case: There is multiple conditions with default state. Show components based on conditions. For example same component has different version. Render it based on version.

function Dashboard({ user, permissions }) {
  let content;

  if (!user) {
    content = <LoginPrompt />;
  } else if (!user.emailVerified) {
    content = <EmailVerificationRequired email={user.email} />;
  } else if (user.subscription === 'expired') {
    content = <SubscriptionExpiredNotice />;
  } else {
    content = <DashboardContent user={user} permissions={permissions} />;
  }

  return (
    <div className="dashboard-container">
      <Header user={user} />
      {content}
    </div>
  );
}

Why this pattern: When you have multiple else-if conditions, it's cleaner to handle them before the return statement rather than nesting ternaries.

Production tip: Use this when logic is too complex for ternaries but doesn't require early returns.


5. Element Variables

Use case: Building dynamic UI elements based on data state. Define elements using JavaScript variables.

function OrderStatus({ order }) {
  // Define UI elements based on order status
  let statusBadge;
  let actionButton;

  if (order.status === 'pending') {
    statusBadge = <Badge color="yellow">Pending Payment</Badge>;
    actionButton = <button>Complete Payment</button>;
  } else if (order.status === 'processing') {
    statusBadge = <Badge color="blue">Processing</Badge>;
    actionButton = <button disabled>Preparing Order...</button>;
  } else if (order.status === 'shipped') {
    statusBadge = <Badge color="green">Shipped</Badge>;
    actionButton = <button>Track Package</button>;
  } else {
    statusBadge = <Badge color="gray">Delivered</Badge>;
    actionButton = <button>Leave Review</button>;
  }

  return (
    <div className="order-card">
      <h3>Order #{order.id}</h3>
      {statusBadge}
      <div className="order-items">
        {order.items.map(item => <OrderItem key={item.id} {...item} />)}
      </div>
      <div className="order-total">${order.total}</div>
      {actionButton}
    </div>
  );
}

Why this pattern: Keeps your return statement clean and readable while handling conditional logic for multiple related elements.

Production tip: Great for when multiple parts of your UI change together based on the same condition.


6. switch Statements

Use case: Role-based access control and rendering different admin panels

function AdminPanel({ userRole, userData }) {
  let panelContent;

  switch (userRole) {
    case 'viewer':
      panelContent = (
        <div className="viewer-panel">
          <h2>Viewer Dashboard</h2>
          <ReportsList reports={userData.reports} readOnly />
          <p className="permission-note">
            You have read-only access. Contact admin for more permissions.
          </p>
        </div>
      );
      break;

    case 'editor':
      panelContent = (
        <div className="editor-panel">
          <h2>Editor Dashboard</h2>
          <ReportsList reports={userData.reports} />
          <CreateReportButton />
          <EditHistoryLog entries={userData.editHistory} />
        </div>
      );
      break;

    case 'admin':
      panelContent = (
        <div className="admin-panel">
          <h2>Admin Dashboard</h2>
          <UserManagement users={userData.allUsers} />
          <ReportsList reports={userData.allReports} />
          <SystemSettings />
          <AuditLogs />
        </div>
      );
      break;

    case 'superadmin':
      panelContent = (
        <div className="superadmin-panel">
          <h2>Super Admin Dashboard</h2>
          <OrganizationManagement />
          <BillingOverview />
          <SystemHealthMonitor />
          <DatabaseBackups />
        </div>
      );
      break;

    default:
      panelContent = (
        <div className="no-access">
          <h2>Access Denied</h2>
          <p>Your account role is not recognized. Please contact support.</p>
        </div>
      );
  }

  return (
    <div className="admin-container">
      <Sidebar role={userRole} />
      <main>{panelContent}</main>
    </div>
  );
}

Why this pattern: More readable than multiple if-else statements when you have 4+ distinct conditions based on a single value.

Production tip: Always include a default case for unexpected values. This prevents rendering nothing or breaking when data is malformed.


Quick summery - When to Use Each Conditional Rendering Pattern

PatternBest ForExample Use Cases
&&Optional elementsBadges, tooltips, conditional features, notification indicators
Ternary (? :)Binary choicesButton text, icon toggles, class names, toggle states
Early ReturnMultiple statesData fetching, authentication checks, validation, loading/error states
if statements2-4 conditions with else-ifComplex logic before return, multi-step authentication flows
Element VariablesMultiple related elements changing togetherKeeping return statement clean, building complex conditional UIs
switch4+ distinct cases from single valueRole-based rendering, status types, notification types, theme selection

Using this your code will be clean and easy to debug. Use this as per requirement. Merge conditions to avoid complexity.

Y
Yashraj4mo ago

Images are good aren't they...

N

Thanks for the tutorial 👍🏼