Mahamudul Hasan Rubel
HomeAboutProjectsSkillsExperienceBlogPhotosContact
Mahamudul Hasan Rubel

Senior Software Engineer crafting high-performance web applications and SaaS platforms.

Navigation

  • Home
  • About
  • Projects
  • Skills
  • Experience
  • Blog
  • Photos
  • Contact

Get in Touch

Available for senior/lead roles and consulting.

bd.mhrubel@gmail.comHire Me

© 2026 Mahamudul Hasan Rubel. All rights reserved.

Built with using Next.js 16 & Tailwind v4

Back to Blog
PerformanceJune 21, 20264 min read

Service Workers: Implementing Stale-While-Revalidate for Web Performance

Service Workers and stale-while-revalidate strategies help you achieve offline-first performance. Learn how to master the Cache API for instant loading.

Service WorkersWeb PerformanceJavaScriptCachingPWAPerformanceWeb VitalsFrontend

Last month, I spent three days fighting a "loading spinner" issue that was driving our users crazy. Our dashboard was fast on a cold cache, but once the service worker installed, it felt like the browser was stuck in a loop of network requests before showing any content. We were relying on standard Cache-Control headers, but they weren't enough for the specific UX requirements we had.

I realized that we needed to stop blocking the UI on network round-trips. That’s when I dove deep into implementing Service Workers with a Stale-While-Revalidate pattern. It changed how our app felt, turning a 600ms wait time into an "instant" render, even if the data was a few seconds out of date.

Why Stale-While-Revalidate is a Game Changer

Most developers lean heavily on the Cache-Control: max-age header. While that's great for static assets, it's a nightmare for dynamic data. If you set a short TTL, you get fresh data but slow performance. If you set a long TTL, you get speed at the cost of stale information.

Stale-While-Revalidate solves this by doing two things at once:

  1. It serves the cached version immediately to the user.
  2. It fetches the latest version in the background and updates the cache for the next visit.

It’s the secret sauce behind many high-performance Progressive Web Apps. By decoupling the display of data from the network fetch, you move the heavy lifting off the critical rendering path.

Implementing the Strategy with the Cache API

To get this working, you need to intercept the fetch event in your service worker. Here is a simplified version of what I put into our production code:

JAVASCRIPT
self.addEventListener(CE9178">'fetch', (event) => {
  if (event.request.url.includes(CE9178">'/api/data')) {
    event.respondWith(
      caches.open(CE9178">'my-cache-v1').then(async (cache) => {
        const cachedResponse = await cache.match(event.request);
        
        // Trigger the background update
        const networkFetch = fetch(event.request).then((networkResponse) => {
          cache.put(event.request, networkResponse.clone());
          return networkResponse;
        });

        // Return the cached response immediately, or wait for the network
        return cachedResponse || networkFetch;
      })
    );
  }
});

The logic is straightforward: check the Cache API first. If it's there, return it instantly. Regardless of whether we found a match, we trigger a network fetch to update the cache.

Avoiding the "Wrong Turns"

I made a mistake early on by trying to update the UI automatically when the background fetch finished. I pushed a socket notification to the client whenever the cache updated, which caused the UI to "flicker" as data changed under the user's feet. It was jarring and confusing.

Instead, I learned that it's better to let the data stay stale until the user performs an action or refreshes. If you absolutely need real-time data, look into Web Workers and OffscreenCanvas for Jank-Free UI Performance to keep your main thread responsive while processing background updates.

Beyond the Basics: Handling Edge Cases

What happens when the network is down? The stale-while-revalidate pattern is inherently resilient. Because you're always checking the cache, the app will continue to work offline.

However, you need to manage your cache size. If you keep adding responses to the cache without ever deleting old ones, you’ll hit storage limits on mobile devices. I implemented a simple cleanup function that runs during the activate event to prune keys that aren't in my current version list.

If your application requires more complex invalidation, you might need to coordinate with server-side logic, similar to how we handle Next.js Cache Invalidation: Mastering Cross-Region Strategies. It’s easy to get lost in the weeds of synchronization, but keep your service worker logic as simple as possible to avoid debugging nightmares.

Frequently Asked Questions

Does stale-while-revalidate hurt SEO? Not directly. Googlebot handles service workers well, but ensure your core content is accessible via standard server-side rendering or static delivery so the crawler doesn't get stuck waiting for a cache update.

How do I know if the cache is getting too big? The CacheStorage API doesn't have a built-in size limit check, so you should monitor navigator.storage.estimate() to see how much space your origin is consuming.

Can I use this for POST requests? No. Service workers only intercept GET requests for the cache. You should never cache the response of a POST request, as those are inherently state-changing operations.

Final Thoughts

Implementing this pattern wasn't a silver bullet. I still had to deal with race conditions and ensure our API endpoints were idempotent enough to handle the background revalidations. If I were doing it again, I’d probably reach for a library like Workbox rather than writing the fetch interceptor from scratch. It handles the edge cases—like partial content and opaque responses—that you don't want to think about at 2 AM.

Service workers are powerful, but they add a layer of complexity that can hide bugs. Start small, verify your cache hits in the DevTools, and don't assume the background fetch always succeeds.

Back to Blog

Similar Posts

PerformanceJune 21, 20264 min read

Speculation Rules API: Achieving Instant Navigation in Modern Apps

Master the Speculation Rules API for predictive prefetching. Learn how to drive instant navigation and improve Core Web Vitals without breaking your server.

Read more
Close-up of colorful CSS code lines on a computer screen for web development.
PerformanceJune 21, 2026
4 min read

Web Workers and OffscreenCanvas for Jank-Free UI Performance

Master Web Workers and OffscreenCanvas to offload heavy rendering tasks from the main thread. Learn how to keep your UI responsive and hit your performance budget.

Read more
PerformanceJune 21, 20264 min read

Cumulative Layout Shift: Advanced Strategies to Stop UI Jitter

Cumulative Layout Shift (CLS) ruins user experience. Learn how to stop UI jitter using CSS containment and aspect-ratio properties for a stable interface.

Read more