useEffect vs useLayoutEffect
Both hooks run side effects after React commits changes to the DOM. The difference is timing relative to the browser painting the screen, and that difference decides whether users see flicker. The React docs for useEffect and useLayoutEffect spell out the useEffect vs useLayoutEffect timing.
1. The commit timeline
render -> commit DOM mutations
-> useLayoutEffect callbacks (synchronous, before paint)
-> browser paints
-> useEffect callbacks (asynchronous, after paint)
2. useEffect
- Runs after the browser has painted.
- Does not block the visual update, so it is the right default for data fetching, subscriptions, logging, timers, and syncing with external systems.
- If it sets state that changes layout, the user may briefly see the “before” state (a flicker).
3. useLayoutEffect
- Runs synchronously after DOM mutations but before paint.
- Any state update inside it is processed before the browser paints, so the user only sees the final result.
- Because it blocks painting, slow work here directly hurts responsiveness.
4. The classic use case: measure then position
function Tooltip({ anchorRect, children }) {
const ref = useRef(null);
const [top, setTop] = useState(0);
... premium content.