queueMicrotask : What It Is and When to Use It
While building a reusable shortcut hints component with TanStack Hotkeys, I hit React's "Cannot update a component while rendering a different component" error. Here's how queueMicrotask fixes it -- and when you should (and shouldn't) reach for it.
queueMicrotask schedules a callback to run after the current synchronous JavaScript finishes, but before the browser paints or processes macrotasks (like setTimeout). It sits in the microtask queue alongside resolved Promises.
Execution Order
Synchronous code -> Microtasks (queueMicrotask, Promise.then) -> Macrotasks (setTimeout, setInterval) -> Browser paint
A Real-World React Bug and Fix
The Problem
You have two React components:
WardsContainerregisters hotkeys usinguseHotkeyfrom TanStack HotkeysHotkeyHintssubscribes to the hotkey manager store to display active hotkeys
useHotkey internally calls setOptions() on the hotkey manager store during render. The store synchronously notifies all subscribers. HotkeyHints has a subscription that calls setHints() (a setState). This means a setState fires in HotkeyHints while WardsContainer is still mid-render.
React throws: Cannot update a component (HotkeyHints) while rendering a different component (WardsContainer).
Without queueMicrotask
useEffect(() => {
const manager = getHotkeyManager();
setHints(readHints(filter));
const sub = manager.registrations.subscribe(() => setHints(readHints(filter)));
return () => sub.unsubscribe();
}, [filter]);
The call chain is fully synchronous:
WardsContainer render
-> useHotkey calls setOptions (synchronous)
-> store notifies subscribers (synchronous)
-> HotkeyHints' setHints fires (synchronous, still inside WardsContainer's render)
-> React error
With queueMicrotask
useEffect(() => {
const manager = getHotkeyManager();
setHints(readHints(filter));
const sub = manager.registrations.subscribe(() => {
queueMicrotask(() => setHints(readHints(filter)));
});
return () => sub.unsubscribe();
}, [filter]);
The synchronous chain is broken:
WardsContainer render
-> useHotkey calls setOptions (synchronous)
-> store notifies subscribers (synchronous)
-> callback queues a microtask (returns immediately)
WardsContainer render finishes
Microtask runs -> setHints fires -> HotkeyHints re-renders
Zero visual delay. The microtask runs before the next paint.
When to Reach for queueMicrotask
1. Breaking synchronous notification chains
Any time an external store subscription fires setState synchronously during another component's render:
store.subscribe(() => {
queueMicrotask(() => setState(store.getSnapshot()));
});
2. Batching multiple synchronous state updates from non-React sources
If an external event emitter fires multiple events in a tight loop:
socket.on("batch-update", (items) => {
items.forEach((item) => {
queueMicrotask(() => updateItem(item));
});
});
React 18+ auto-batches setState calls in the same microtask, so all updates consolidate into one render.
3. Deferring work that must happen before paint but after current execution
function handleClick() {
setOptimisticState(newValue);
queueMicrotask(() => {
validateAndCorrectIfNeeded(newValue);
});
}
queueMicrotask vs Alternatives
| Approach | Timing | Use When |
|---|---|---|
queueMicrotask |
After sync, before paint | Breaking sync chains, need immediate-but-deferred |
Promise.resolve().then() |
Same as microtask | Same scenarios (microtask is cleaner and more explicit) |
setTimeout(fn, 0) |
After paint (macrotask) | Truly deferring to next event loop tick, OK with visual delay |
requestAnimationFrame |
Before next paint | Animation-related work |
React.startTransition |
Low-priority React update | Keeping UI responsive during expensive re-renders |
When NOT to Use It
- Inside React event handlers -- React already batches these. No deferral needed.
- For heavy computation -- It blocks the paint just like synchronous code would. Use
setTimeoutor Web Workers instead. - As a general "fix React warnings" tool -- It masks the symptom. Only use it when you genuinely need to break a synchronous call chain from an external store.