What does "flushSync()" do?
Definition
flushSync(callback)is a function from thereact-dompackage that forces React to immediately "flush" all pending state updates inside the given callback and synchronously update the DOM.
import { flushSync } from 'react-dom';
flushSync(() => {
setState(newValue);
});After flushSync() returns, React guarantees that:
- the component has already re-rendered;
- the DOM is updated;
- all layout effects (
useLayoutEffect) for that update have already run.
1. Why it is needed at all
By default, React works asynchronously, especially in React 18 with Concurrent Rendering. It can:
- defer updates;
- combine several
setStatecalls into one render (batching); - not update the DOM immediately.
Sometimes this creates situations where you need to see the result in the DOM right away, for example:
- to measure an element (
offsetHeight); - to smoothly kick off an animation;
- to set focus;
- to react synchronously to a user action.
2. Example: without flushSync
function Example() {
const [count, setCount] = useState(0);
const divRef = useRef();
const handleClick = () => {
setCount(c => c + 1);
console.log(divRef.current.textContent); // old value
};
return <div ref={divRef}>{count}</div>;
}What happens:
setCountputs the update in the queue;- React schedules a render but does not run it instantly;
- the log runs before the DOM update -> you see the old value.
3. Example with flushSync
import { flushSync } from 'react-dom';
function Example() {
const [count, setCount] = useState(0);
const divRef = useRef();
const handleClick = () => {
flushSync(() => {
setCount(c => c + 1); // state update
});
console.log(divRef.current.textContent); // already the new value
};
return <div ref={divRef}>{count}</div>;
}Now React:
- Immediately runs
setCount(); - Synchronously runs render and commit;
- Updates the DOM;
- Only then returns from
flushSync; - The log shows the current
count.
4. How it works under the hood
flushSync:
- temporarily disables concurrent batching;
- forces React to process all pending updates (including ones scheduled earlier);
- immediately runs the commit phase, updating the DOM and layout effects;
- then returns React to its normal asynchronous mode.
Without flushSync:
setState() → wait → React decides when to update
With flushSync:
setState() → render right away → commit → ready DOM5. Where you need to apply this
| Scenario | Why flushSync is needed |
|---|---|
Need to measure the DOM after setState | To get accurate sizes |
| Starting an animation right after a change | The DOM must be updated instantly |
| Focusing or scrolling after an update | So the element definitely exists in the DOM |
| Working with external libraries (GSAP, D3, Chart.js) | These libraries expect the current DOM state |
| Sometimes - when integrating with React Transition Group, Portal, or drag-and-drop | When the commit needs to happen synchronously |
6. But use it carefully!
React deliberately makes rendering asynchronous in order to:
- avoid blocking the interface,
- optimize batching of updates,
- improve UI responsiveness.
flushSync() turns off these benefits for a specific piece of code.
Do not do this:
flushSync(() => {
setA(1);
});
flushSync(() => {
setB(2);
});Each call forces React to render and commit again, which hurts performance.
It is better to group several updates into a single flushSync().
7. Interaction with the lifecycle
| Stage | What happens with flushSync() |
|---|---|
| Render Phase | Runs immediately for all changed components |
| Commit Phase | Happens synchronously |
Effects (useLayoutEffect) | Run right after commit |
Effects (useEffect) | Still run asynchronously (on the next tick) |
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.