Hint
useEffect fires after paint; useLayoutEffect fires synchronously before paint — use for DOM measurements
Both run after render but at different times:
// useEffect — paint happens first, then effect
// Causes flicker if you update DOM based on measurement
useEffect(() => {
const height = ref.current.offsetHeight; // reads layout
setHeight(height); // triggers second render → visible flicker!
}, []);
// useLayoutEffect — DOM updated, effect runs, THEN paint
// No flicker because browser hasn't painted yet
useLayoutEffect(() => {
const height = ref.current.offsetHeight;
setHeight(height); // only one paint with correct height
}, []);
Sequence:
render → DOM mutations → useLayoutEffect → browser paint → useEffect