SyntaxStudy
Sign Up
React Rendering Nothing and Toggling Visibility
React Beginner 2 min read

Rendering Nothing and Toggling Visibility

Returning `null` from a component is a valid way to render nothing to the screen while keeping the component in the tree and preserving its state. This differs from toggling a CSS class such as `display: none` in that the element is absent from the DOM entirely when `null` is returned, saving memory and preventing hidden elements from being discovered via DOM inspection. For elements that are expensive to create (large forms, rich editors), CSS toggling preserves state between show/hide cycles; for elements that should reset on each open, returning `null` is preferable. Mounting and unmounting components with conditional rendering runs React's cleanup logic: effects with cleanup functions are cleaned up when a component unmounts, and state is reset to the initial value on the next mount. This is the mechanism behind closing a modal and having it open fresh on the next click. Awareness of this behaviour helps you decide between "show/hide with CSS" versus "mount/unmount" for any togglable UI element. Transitions and animations require extra care with conditional rendering because CSS transitions do not apply to elements that are instantly removed from the DOM. Solutions include keeping the element mounted and using CSS classes to animate in and out, using the `` component from react-transition-group, or using Framer Motion for declarative spring animations. The right choice depends on the complexity of the animation and the project's existing dependencies.
Example
import { useState } from 'react';

// null return — component absent from DOM
function Tooltip({ visible, text, children }) {
  return (
    <span style={{ position: 'relative', display: 'inline-block' }}>
      {children}
      {visible && (
        <span style={{
          position: 'absolute', bottom: '130%', left: '50%',
          transform: 'translateX(-50%)',
          background: '#333', color: '#fff',
          padding: '4px 10px', borderRadius: 6,
          whiteSpace: 'nowrap', fontSize: 13,
        }}>
          {text}
        </span>
      )}
    </span>
  );
}

// Modal: returning null vs CSS hidden
function Modal({ isOpen, onClose, children }) {
  if (!isOpen) return null; // unmount — state inside resets on next open
  return (
    <div style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.5)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
      <div style={{ background: '#fff', padding: 32, borderRadius: 12, minWidth: 300 }}>
        {children}
        <button onClick={onClose} style={{ marginTop: 16 }}>Close</button>
      </div>
    </div>
  );
}

function App() {
  const [tip, setTip] = useState(false);
  const [open, setOpen] = useState(false);

  return (
    <div style={{ padding: 32 }}>
      <Tooltip visible={tip} text="This is a tooltip!">
        <button onMouseEnter={() => setTip(true)} onMouseLeave={() => setTip(false)}>
          Hover me
        </button>
      </Tooltip>

      <button onClick={() => setOpen(true)} style={{ marginLeft: 16 }}>
        Open modal
      </button>

      <Modal isOpen={open} onClose={() => setOpen(false)}>
        <h2>Hello from Modal</h2>
        <p>This content is unmounted when closed.</p>
      </Modal>
    </div>
  );
}

export default App;