A component measures a node after an update and writes a corrected position from it, and users see a one-frame flash. Why?
answer
- two tiers, one before the frame, one after
- the flash is two painted states
- references are populated during commit
- pre-paint work delays every interaction
basics
~20 sThe measure-and-write ran in the callback tier that fires after the host presented the frame, so the uncorrected position was painted once and the correction became a second visual state. The pre-paint tier inside the commit avoids it.
solid answer
~50 sA commit ends with two callback tiers. One runs synchronously while the commit is still in progress — nodes are written and references populated, but the host has not yet presented the new frame. The other runs after the frame is on screen. Code that reads a node's geometry and then writes a style derived from it belongs in the first tier: the write lands in the same frame as the update and the user never sees the intermediate state. In the second tier, the uncorrected layout is painted first, so the correction is a visible second state — the flash. The trade is that the pre-paint tier sits between the update and the pixels, so anything slow there delays every frame; keep it to read-then-write corrections that must not be seen, and leave data fetching, subscriptions and logging in the deferred tier.
go deeper
Remember that a framework gives you more than one place to run code after an update, and that one of them runs before the user sees the new frame. That choice is what causes or avoids a flash.
Explain the commit order — mutations, references, pre-paint callbacks, presentation, deferred callbacks — and why a read-then-write correction in the deferred tier produces two visible states.
Show the judgment: the pre-paint tier is a shared budget on the critical path, so only corrections that must not be seen belong there. Say how you would spot a page that has overused it.
Argue for removing the measurement instead of relocating it. A correction the host resolves during its own layout has no wrong frame and no per-component script cost to police.
## Where the two tiers sit The commit is ordered, and the two side-effect tiers straddle the moment the host presents a frame: 1. Teardown for instances that are going away. 2. **Mutations**: nodes created, properties written, children moved, subtrees removed. 3. **References populated** — so a node is already reachable from component code at this point, not later. 4. **The synchronous, pre-paint tier** runs. The commit is still executing; the host has not presented the new frame. 5. The host presents the frame. 6. **The deferred tier** runs, after the pixels are on screen. That ordering is the whole answer to the flash. Both tiers can read a node — the reference is populated in step 3 — but only one of them can write a correction that the user never sees. ## Why the flash happens A read-then-write correction has three parts: the update lands, the code measures, the code writes. If the measure-and-write happens in the deferred tier, the sequence the user experiences is: - frame N: the update's own output, uncorrected, painted; - then script runs, measures, writes; - frame N+1: the corrected output. Two visually different frames for one logical change. At 60 frames per second that is roughly 16 ms of wrong position — clearly visible for anything that moves a noticeable distance, and worse when the write changes size, because neighbouring content shifts too. In the pre-paint tier the same three parts all complete before step 5, so frame N is already correct and there is no second state. | Tier | Runs | Reference available | Correction visible as a flash? | Blocks the frame? | |---|---|---|---|---| | Pre-paint, synchronous | inside the commit, before presentation | yes | no | yes — the frame waits for it | | Deferred | after the frame is presented | yes | yes, for one frame | no | ## Why not put everything in the pre-paint tier Because it is on the critical path. Every millisecond spent there is a millisecond the user waits for the visual result of their interaction, and the work is not interruptible — the commit is a single pass. A page whose components all measure before paint has a response time that grows with its component count, and the symptom is a sluggish app with no single slow function. A usable rule: - **Pre-paint tier**: read-then-write corrections whose intermediate state would be seen — positioning a floating element against an anchor, clamping a scroll offset, restoring a scroll position, adjusting for text that just changed size. - **Deferred tier**: everything else — starting a request, subscribing, logging, analytics, syncing to storage, and measurements whose result only affects a later frame. ## The better fix, when it is available Measuring at all is a cost, and the framework cannot make it free: reading geometry forces the host to have an up-to-date layout, and that mechanic belongs to the host rather than to the framework. So before reaching for the pre-paint tier, ask whether the correction can be expressed declaratively — as a style the host resolves itself, an anchoring or containment feature of the host's layout system, or a value derived from data you already have. A correction the host performs during its own layout never needs a script measurement and never has a wrong frame to fix. When measurement is unavoidable, the remaining discipline is to do all the reading before any of the writing inside that callback, so the host's layout is settled once rather than repeatedly. The details of why interleaving reads and writes is expensive are the host's story, not the framework's; what the framework owns is *which phase* your read and write run in. ## Two more traps in the same area - **Reading in the wrong order inside the pass.** A reference is populated during the commit, so reading it during the compute half — before any mutation — gives the previous frame's node or nothing at all. - **Triggering a new update from the pre-paint tier.** It works: the runtime re-enters and commits again before presenting. But it doubles the pre-paint work, and a condition that keeps re-triggering becomes a loop that never lets a frame out. ## Where frameworks differ Every runtime with a commit has some notion of *before the frame is shown* versus *after*, but the names and the number of tiers vary, and some expose only one tier plus an explicit synchronous flush. A fine-grained runtime may apply a write so directly that the distinction collapses for simple cases, while still needing an ordered hook for anything that measures. The reasoning does not change: if the user must never see the uncorrected state, the write has to happen before the host presents the frame.
- How would you spot a page that has overused the pre-paint tier?Interactions feel uniformly sluggish with no single slow function, and the delay scales with how much of the tree updated rather than with what the user did. Profiling shows script time clustered between the update and the frame, spread across many small callbacks. The fix is triage: each one must justify being on the critical path by having an intermediate state the user would otherwise see.
- Is reading geometry in the deferred tier always wrong?No. It is wrong only when the value feeds a write whose intermediate state would be visible. Measuring to report a size, to decide what to load next, to cache a layout for a future interaction, or to drive something that changes on a later frame anyway is perfectly at home after presentation, and it keeps the critical path short.
- What happens if the pre-paint tier triggers another update?The runtime re-enters and commits again before the host presents anything, so the user still sees one frame — correct, but arrived at twice. It doubles the work on the critical path, and a condition that keeps re-triggering never lets a frame out at all, which presents as a frozen tab rather than a slow one.
saying these in an interview costs you the question
- Measures in the post-paint tier and blames the flash on the measurement cost
- Thinks a node reference is only usable after the host has painted
- Moves all side effects into the pre-paint tier to be safe
- Says the flash is a host bug rather than an ordering choice
- Reaches for a script measurement when the host's layout could position it