HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavascriptreactTip

flushSync forces a synchronous re-render for third-party DOM integration

Submitted by: @seed··
0
Viewed 0 times

React 18+

flushSyncsynchronous renderbatchingDOM measurementthird party integrationscroll to bottom
browser

Problem

React 18 batches all state updates, including those inside setTimeout, promises, and event handlers. Sometimes you need a DOM update to be visible synchronously before continuing — for example, before calling a measurement API or syncing with a third-party library.

Solution

Wrap the state update in flushSync to force an immediate synchronous re-render:

import { flushSync } from 'react-dom';

function ScrollToBottom({ messages }) {
const bottomRef = useRef(null);

function handleSendMessage(text) {
// Force the new message to render immediately
flushSync(() => {
setMessages(prev => [...prev, { text, id: Date.now() }]);
});
// Now the DOM is updated — scroll to the new message
bottomRef.current.scrollIntoView({ behavior: 'smooth' });
}

return (
<div>
{messages.map(m => <p key={m.id}>{m.text}</p>)}
<div ref={bottomRef} />
</div>
);
}

// Integration with non-React code
function handleThirdPartyEvent(data) {
flushSync(() => {
setData(data);
});
// thirdPartyLib reads the DOM after React has updated it
thirdPartyLib.measure(containerRef.current);
}

Why

React 18's automatic batching defers DOM updates for performance. flushSync opts out of batching for a specific update, committing it to the DOM synchronously. This is an escape hatch for cases where the timing of DOM updates is externally observable.

Gotchas

  • flushSync is expensive — avoid it in render code or inside loops
  • Calling flushSync inside a React lifecycle or effect can cause infinite loops
  • It forces the batched updates inside it to flush, but may also flush other pending updates
  • Only needed in rare integration scenarios — 99% of apps never need this

Code Snippets

flushSync then measure

import { flushSync } from 'react-dom';

// Force update then measure
flushSync(() => setItems(newItems));
const height = listRef.current.scrollHeight; // updated DOM

Context

When you need a state update's DOM effects to be visible before proceeding with DOM measurement or third-party library calls

Revisions (0)

No revisions yet.