How I Reduced Firebase Loading Time from 5 Seconds to Under 100ms

Published: (March 26, 2026 at 07:22 AM EDT)
3 min read
Source: Dev.to

Source: Dev.to

The Problem

When Muslifie launched, tour listing pages were taking 2–5 seconds to load. Users on slower connections in Pakistan, Egypt, and Indonesia were bouncing before the data even appeared.

The culprit? Every time a user opened the app, Firebase ran fresh Firestore queries — no caching, no batching, just raw reads on every open.
At 200+ guide profiles and growing, this was getting expensive and slow.

My first instinct was client‑side caching with Flutter’s built‑in Firestore persistence. It helped a little, but:

  • First load was still slow
  • Data was stale on reinstall
  • No control over cache invalidation

Not good enough for a marketplace where guide availability changes daily.

The Solution

Move the heavy lifting to Firebase Cloud Functions with in‑memory caching.

// functions/index.js
let cachedData = null;
let cacheTime = null;
const CACHE_TTL = 5 * 60 * 1000; // 5 minutes

exports.getTours = functions.https.onCall(async (data, context) => {
  const now = Date.now();

  // Return cache if still fresh
  if (cachedData && (now - cacheTime)  ({ id: doc.id, ...doc.data() }));
  cacheTime = now;

  return cachedData;
});

Key insight: Cloud Function instances stay warm between calls, so the cache lives in memory as long as the instance is alive — typically 15–30 minutes.

The first user to hit the function after a cold start waits ~400 ms. Every subsequent user gets cached data in under 100 ms.

Metrics

MetricBeforeAfter
Initial load2–5 secondsUnder 100 ms
Firestore reads/day40,000+~800
Firebase billRising fastFlat

Perceived Performance

The performance fix only works if users don’t stare at a blank screen during that first cold start. I paired it with skeleton loaders in Flutter:

// lib/widgets/tour_list.dart
Widget build(BuildContext context) {
  return FutureBuilder>(
    future: fetchTours(),
    builder: (context, snapshot) {
      if (!snapshot.hasData) {
        return TourSkeletonList(); // Show skeleton while loading
      }
      return TourList(tours: snapshot.data!);
    },
  );
}

Perceived performance matters as much as actual performance. Users tolerate loading if they can see something happening.

Future Improvements

If I were building Muslifie today, I’d add:

  • Redis caching via Firebase Extensions for multi‑instance cache sharing
  • Incremental loading — show top 10 tours instantly, load the rest in the background
  • Prefetch on app launch — start fetching data the moment the app opens, before the user even navigates

Takeaways

  • Don’t optimize early; profile first.
  • The bottleneck wasn’t the Flutter widgets or network speed—it was unnecessary Firestore reads.
  • A simple 20‑line caching function can dramatically reduce reads and cost.
  • Add logging to every data fetch and look at where time is actually spent; the answer is usually simpler than you think.

Originally published at buildzn.com.

0 views
Back to Blog

Related posts

Read more »