Push-based vs. Pull-based Reactivity: The Two Driving Models Behind Fine-Grained Systems

Published: (December 14, 2025 at 07:40 PM EST)
4 min read
Source: Dev.to

Source: Dev.to

Cover image for Push-based vs. Pull-based Reactivity: The Two Driving Models Behind Fine-Grained Systems

Recap

Building on the previous article about the core ideas behind reactivity, this part clarifies the difference between Push‑based and Pull‑based reactivity models.

Core Idea

In fine‑grained reactivity:

  • Push‑based systems perform computation immediately when a value changes.
  • Pull‑based systems delay computation until the moment someone reads the value.

Real‑World Examples

Push‑based

Imagine ordering food in a food court.

  • Write (ordering): You place your order.
  • Push (notify on completion): When your meal is ready, your buzzer vibrates or lights up — the update is pushed all the way to you.
  • Effect (pick up): You walk to the counter to pick it up.

Reactivity interpretation: When a source changes, dependent nodes are recomputed immediately and notified right away.

Pull‑based

Now imagine buying a bubble tea.

  • Write (ordering): You place your drink order.
  • Mark (state updated only): When the drink is ready, the shop just posts your number on a screen — they don’t notify you directly.
  • Read → compute (only when needed): When you look up the screen, that read operation triggers “oh, it’s ready, I should go pick it up.”
  • Effect (pick up): You go to the counter.

Reactivity interpretation: Writes only mark nodes as dirty; real computation happens later when someone reads the value.

Formal Definitions

ModelBehavior FocusSimplified Flow
Push‑basedCompute on write: updates propagate immediatelyset() → propagate → compute → effect
Pull‑basedMark on write, compute on readset() → markDirty ⏸ read() → if dirty → compute → effect

Key insight: Both models “push” signals — Push pushes the computation, while Pull pushes the dirty mark.

Timeline Diagrams

Push‑based

push flow

Pull‑based

pull flow

Pros & Cons

AspectPush‑basedPull‑based
Read latencyLowest — always freshFirst read may trigger recomputation
Write costPotentially high: O(depth × writes)Lower: mostly O(depth × 1) (mark dirty only)
Over‑computationHigh — computes even if never readLow — compute only when actually read
BatchingHard — work is already doneNatural fit — flush later in one batch
Debug visibilityDependency chain expands immediatelyNeed DevTools to inspect when pulling happens
Best use casesHigh‑frequency writes, low reads (e.g., cursor syncing in collaboration apps)Low writes, high reads (dashboards, charts)

Note: Pull‑based systems still walk the dependency graph during marking, but they do not recompute — they only set dirty = true.

How to Choose?

Use CaseRecommended ModelReason
Real‑time collaboration, game state syncPushImmediate reflection is more important than avoiding extra recomputation
Large dashboards, data visualizationsPullWrites are rare, reads are frequent — compute only what is actually needed
Timeline / scroll‑driven animationsPull + SchedulerPull defers work; scheduler ensures recomputation happens at most once per frame
Data pipelines (expensive computations reused many times)Push‑on‑CommitCompute once upfront, then reuse everywhere

React is basically Pull + Scheduler, which is why batching works the way it does.
RxJS and MobX are classic examples of Push‑on‑Commit.

Common Misconceptions

  • “Pull means scanning the whole graph!”
    No — pull only checks the relevant dependency chain upward when a value is read. No full‑tree traversal is required.

  • “Push always wastes computation.”
    Not when the result is guaranteed to be consumed immediately (e.g., cursor movement). Lower read latency can outweigh the cost of extra writes.

  • “Push vs. Pull is either/or.”
    Most modern signal systems use a hybrid approach:
    Push during writes → propagate dirty flags
    Pull during reads → compute only when needed.
    This combines responsiveness and laziness.

  • “Why do even fine‑grained systems still need Pull?”
    Because we often don’t know:

    • Will this value ever be read?
    • When will it be read?
      Pull separates “the data changed” (mark dirty) from “do I need to act on this?” (compute on read).

Conclusion

Why does fine‑grained reactivity need a Push vs. Pull discussion?

In coarse‑grained systems (like React’s Virtual DOM), diffing the whole tree is abstract enough.
But in signal‑based systems, a single set() may fan out to hundreds of tiny derivations.

Choosing when computation happens affects:

  • Total compute cost (performance)
  • Interaction latency (UI smoothness)
  • Scheduling behavior (avoiding jitter & dropped frames)

Understanding Push vs. Pull gives you the mental model and vocabulary needed to evaluate different reactivity frameworks.

Next Up

In the next article, we’ll explore how different mainstream frameworks design their reactivity systems — and why they made those choices.

Back to Blog

Related posts

Read more »