Conditional Rendering in React: 7 Patterns You Should Know

Posted November 17, 2025 by Karol Polakowski

Conditional rendering is a fundamental part of building interactive UIs in React. As components grow, deciding how and where to express conditional logic can affect readability, testability, and performance. Below are seven practical patterns you can use depending on the complexity and intent of your UI.


Inline if with logical &&

Use the logical AND when you want to render something only when a condition is truthy. This is compact and excellent for simple optional UI pieces.

Example:

function Notifications({ items }) {
  return (
    <div>
      {items.length > 0 && <ul>
        {items.map(item => <li key={item.id}>{item.text}</li>)}
      </ul>}
    </div>
  );
}

When to use: small, single conditional fragments. Avoid when the falsy value (0, “”) is meaningful because it will short-circuit rendering.

Pros: concise. Cons: can be unclear when used with non-boolean values.

Ternary operator for two branches

The ternary is the natural choice when there are two distinct outcomes (if/else) to render.

Example:

function UserGreeting({ user }) {
  return (
    <div>
      {user ? <span>Welcome, {user.name}!</span> : <button>Sign in</button>}
    </div>
  );
}

When to use: clear two-way choices. Avoid chaining multiple ternaries — they become hard to read.

Element variables (precompute a component or fragment)

Assign a component or JSX to a variable. Use this when you need to compute what to render and then incorporate it into larger JSX.

Example:

function Profile({ user, loading }) {
  let content;

  if (loading) {
    content = <div>Loading...</div>;
  } else if (!user) {
    content = <div>No user found.</div>;
  } else {
    content = <div>Hello, {user.name}</div>;
  }

  return <section>{content}</section>;
}

When to use: multiple conditional branches where you still want to keep the returned JSX simple. Improves readability by separating decision-making from markup.

Early return (guard clauses)

Return early from the component for simple guard conditions. This flattens nested conditionals and keeps the happy path prominent.

Example:

function SearchResults({ query, results }) {
  if (!query) return <div>Type to search</div>;
  if (!results) return <div>Searching...</div>;
  if (results.length === 0) return <div>No results</div>;

  return (
    <ul>
      {results.map(r => <li key={r.id}>{r.title}</li>)}
    </ul>
  );
}

When to use: validate inputs or states early, keep the main return concise.

Pros: easy to follow. Cons: lots of small returns may be unfamiliar to some teams.

switch or object map for multiple branches

When you have many discrete states, use a switch statement or an object mapping to keep conditions organized.

Example with switch:

function StatusMessage({ status }) {
  switch (status) {
    case 'loading':
      return <div>Loading...</div>;
    case 'success':
      return <div>Success!</div>;
    case 'error':
      return <div>Something went wrong.</div>;
    default:
      return <div>Idle</div>;
  }
}

Example with object map (useful for simple static mappings):

const messages = {
  loading: <span>Loading...</span>,
  success: <span>Success!</span>,
  error: <span>Error</span>
};

function Status({ status }) {
  return <div>{messages[status] || <span>Idle</span>}</div>;
}

When to use: many states or custom UI per state. Mapping scales well and avoids long if-else chains.


Higher-Order Component (HOC) for cross-cutting concerns

Extract conditional behaviors (loading, authorization, feature flags) into HOCs that wrap components, keeping the wrapped component focused on rendering.

Example (withLoading HOC):

function withLoading(Wrapped) {
  return function WithLoading(props) {
    if (props.loading) return <div>Loading...</div>;
    return <Wrapped {...props} />;
  };
}

function DataView({ data }) {
  return <div>{data.title}</div>;
}

const DataViewWithLoading = withLoading(DataView);

When to use: reuse conditional behavior across many components. Note: prefer hooks or composition when possible in new code.

Render props / function-as-child for dynamic rendering

Pass a function that decides what to render based on internal state. This is powerful when the parent needs to control rendering while the child manages state or behavior.

Example:

function Toggle({ children }) {
  const [on, setOn] = React.useState(false);
  return children({ on, toggle: () => setOn(v => !v) });
}

function App() {
  return (
    <Toggle>
      {({ on, toggle }) => (
        <div>
          <button onClick={toggle}>Toggle</button>
          {on ? <div>ON</div> : <div>OFF</div>}
        </div>
      )}
    </Toggle>
  );
}

When to use: you need the consumer to decide how to render internal state or you want to share behavior while keeping rendering flexible.

Pros and cons summary

  • &&: concise for single optional pieces, but beware of falsy values.
  • Ternary: explicit two-way choices; avoid nesting.
  • Element variables: keeps return clean for complex decisions.
  • Early returns: flatten logic and prioritize the main path.
  • switch / map: scales to many discrete states.
  • HOC: good for cross-cutting conditional behaviors; somewhat older pattern in hooks era.
  • Render props: flexible and composable; can be replaced by hooks + composition in many cases.

Best practices

  • Keep conditional logic close to the rendering when it’s simple. Extract into helpers or variables when it grows.
  • Prefer composition (wrappers, small components) over long inline conditional logic.
  • Avoid deep nesting of ternaries; prefer variables, early returns, or mapping instead.
  • Consider accessibility: ensure conditional elements are announced or have appropriate roles when they appear/disappear.

Conclusion

Choosing the right conditional pattern depends on context: complexity, reuse, and readability. Use simple inline patterns for trivial cases and extract to variables, HOCs, or render props when you need reuse or clearer structure. With these seven patterns you should be able to keep your components both expressive and maintainable.