Meteor 3 Blaze UI Flicker

I’m upgrading a Meteor 2.16 app to Meteor 3.3. We’ve converted all our client Meteor.call() to await Meteor.callAsync(), we’ve updated our server mongo functions to Collection.findOneAsync(), etc., we’ve made publication functions async when needed, etc. etc. It’s all working quite well actually.

This biggest glaring problem though now is in Meteor 2.16 there was no template flicker on the client at all after doing a Meteor.call() to update data that was published and subscribed to and returned by various helpers who’s dependencies are this.field that was delivered via a minimongo query.

In Meteor 3.3, data client updates are done through await Meteor.callAsync() calls to Meteor Methods that live in common code mostly. Now, the client template always flickers. For example, if I’m updating some boolean value in document.field to true, using await Meteor.callAsync(), reflecting that boolean value on the client using a template helper will show the boolean going true optimistically, then back to false, then back to true.

All the Meteor 3 Blaze changes doc discusses is awaiting helpers that return promises and that’s not our pattern here. There’s not much other mention of this.

I’ve also tried using the async/await with findOneAsync pattern on the client minimongo queries but I get the same result. And it also happens regardless if the actual helper function is async or not.

I’m surprised this isn’t being discussed more. I know there’s a ton of threads on this in the past, but we never really saw this issue in Meteor 1.x and 2.x.

I don’t use Blaze, but I started with Blaze 10 years ago, and I am still curious about it.
Reading what Perplexity says about it, is it possible that your server returns a false (falsy) and then a true?
Another thing I can think of is the isomorphic calls vs client calls. Check this out (no use of “async”): Collections | Docs
Similarly, stubbed methods: Meteor API | Docs


Page flickering when performing optimistic UI updates in Meteor 3 with Blaze typically happens due to how Meteor/Minimongo handles “latency compensation.” Here’s why this flickering can occur, especially when using Blaze:

  • Optimistic Update Workflow: When you update a value optimistically (e.g., calling a Meteor method that updates a document), Minimongo simulates this update on the client immediately, so you see a fast UI update[1][2].
  • Server Reconciliation: When the server responds, Minimongo undoes the client-side (simulated) update and reapplies the server’s official data. This is necessary in case the server’s data differs (for example, if there are permission differences or server-only logic like setting updated = new Date).
  • Resulting Flicker: If the server’s response is different from your optimistic update (even a minor difference, like a timestamp or field not set identically), the value will “snap back” to the original state, then update again when the server-confirmed value arrives. This causes the visible flicker or flash you observe[2].

Why Flicker Happens with Blaze

Blaze’s data reactivity means it immediately re-renders when Minimongo’s data changes. If the simulated client value gets rolled back and then replaced with the server value (especially in separate computation ticks), Blaze re-renders twice in rapid succession—producing a noticeable flicker[3][2].

This sequence is often:

  1. Value updates optimistically.
  2. Value reverts (rollback before new server data arrives).
  3. Value updates again (with server response).

How to Minimize Flicker

  • Batch Minimongo Updates: Meteor tries to use pauseObservers and resumeObservers around bulk Minimongo changes so the UI only sees a single, final change, rather than a batch of intermediate steps. If something breaks this batching, flicker is more likely[3].
  • Match Server & Client Logic: The less your client “guesses” incorrectly (e.g., if you set fields like timestamps or computed properties on the server), the less Minimongo will need to roll back and reapply different data.
  • Blaze and Async: Meteor 3 introduced more async patterns and some cases where reactivity is dropped after await inside helpers. Ensuring you stay as “reactive” as possible and testing with Tracker.withComputation can help, but sometimes you may need to restructure UI code to avoid rendering based on intermediate data[4].
  • Control When to Show UI Changes: Sometimes you may want to pessimistically wait for the server, or use local state or session variables to mask the rollback and reduce flicker for specific critical fields.

In summary: The flicker is a side effect of how Meteor ensures consistency between client and server with optimistic UI updates, especially when client-side predictions don’t perfectly match server results. Precision in your client simulation and careful Blaze reactivity management can help reduce this effect[1][3][2][4].

Sources
[1] Optimistic UI with Meteor Optimistic UI with Meteor. In this post we’ll look at the… | by Sashko Stubailo | Meteor Blog
[2] Flicker from optimistic UI and partially simulated methods Flicker from optimistic UI and partially simulated methods
[3] minimongo minimongo - Packosphere
[4] Migrating to Meteor 3: a solo developer story Migrating to Meteor 3: a solo developer story - DEV Community
[5] In-Place Optimistic UI https://www.youtube.com/watch?v=Z07vhWKpfXs
[6] Delay in state updating causes the UI to “flicker”, how to … Delay in state updating causes the UI to "flicker", how to avoid this? · Issue #873 · atlassian/react-beautiful-dnd · GitHub
[7] Dynamic imports cause Blaze UI breakage in Safari 10/11 … Dynamic imports cause Blaze UI breakage in Safari 10/11 (with reproduction) · Issue #9926 · meteor/meteor · GitHub
[8] Managing optimistic UI updates and clobbering : r/reactjs https://www.reddit.com/r/reactjs/comments/cl2o7u/managing_optimistic_ui_updates_and_clobbering/
[9] The Gitbook Meteor Guide | PDF | Java Script The Gitbook Meteor Guide | PDF | Java Script | Application Software

I did a meteor create meteor3-blaze-test --full in order to troubleshoot optimistic UI in a simple Blaze app in Meteor 3 and I can report it all seems to work perfectly with no flicker.

I created the following Meteor method that is loaded on both the client and server along with a simple helper function that adds a red/green background class depending on link.disabled:

'links.toggleDisabled': async function(linkId) {
    check(linkId, String);

   // Confirm this logs in both client and server consoles
   console.log("links.toggleDisabled: running...");

   // Test doing a query just for complexity
   const link = await Links.findOneAsync(linkId);
   if (!link) {
     throw new Meteor.Error('link-not-found', 'Link not found');
   }

   if(Meteor.isServer) {
      console.log("links.toggleDisabled: server-only code running...");

      // Since Meteor._sleepForMs no longer works in Meteor 3, use this to 
      // cause a delay
      await new Promise(resolve => setTimeout(resolve, 2000));

      // Optionally throw a testing error to see if optimistic UI gets backed out
      throw new Meteor.Error('simulated-error', 'This is a simulated error for testing purposes');
    }

    //  Toggle the disabled state of the link
    return await Links.updateAsync(
      {_id: linkId},
      {$set: {disabled: !link.disabled}}
    );
  },

There’s two cases tested:

  • Server Throws Error: The link changes color for two seconds, then reverts back. With no flicker.
  • No Server Error: The link changes color instantly and does not flicker.

It’s also worth noting that in the test app, it uses Links.find() in a helper to return subscribed Links, so its template code is not isomorphic and there’s still no flicker.

Upon looking at the DDP logs for a simple Method call, there’s way more activity in my app than this test test app. So it’s something wonky I’m doing, that didn’t cause this in Meteor 2.x.

I went to work tearing my app apart - there’s pieces thrown all over the place! I removed matb33:collection-hooks, ccorcos:subs-cache, I wrote a simplified route, template and methods, just like the test app and still… the flicker! :face_with_symbols_over_mouth:

Then I added cultofcoders:redis-oplog to the test app (as it’s hard to remove from our main app due to our heavy use of Vent) and bam, I see the flicker. Well, I don’t actually see the flicker because the app’s DOM is so simple that the browser doesn’t show it. But logging the helper shows the value of the link.disabled field now bouncing between true, false, and true just like my own app.

So now digging into cultofcoders:redis-oplog to see what the issue is. I know in the past there were lots of threads on optimistic UI and this package, but it all seemed to be resolved. As my own app uses optimistic UI and cultofcoders:redis-oplog and never flickered on Meteor 2.x.

EDIT: It’s the combination of cultofcoders:redis-oplog@3.0.1 and montiapm:agent@3.0.0-beta.15 together. :skull: Confirmed in the test app too. Reaching out to montiapm. Weirdly, the bug doesn’t happen when using either one individually.