● LIVE   Breaking News & Analysis
Ehedrick
2026-05-20
Open Source

How to Modernize Navigation Performance in Your Web App: A Step-by-Step Guide

Learn to modernize web app navigation performance using client-side caching, IndexedDB, preheating, and service workers. Step-by-step guide with real-world examples from GitHub Issues.

Introduction

In developer tools, every millisecond counts. When you're triaging issues, jumping between threads, or reviewing code, even tiny delays break your flow. GitHub Issues faced this problem: too many navigations paid the cost of redundant data fetching. The solution wasn't marginal backend tweaks but a client-side revolution. This guide shows you how to apply the same patterns—client-side caching, preheating, and service workers—to make your data-heavy web app feel instant. You'll learn step by step how to shift work to the client, render instantly from locally available data, and revalidate in the background.

How to Modernize Navigation Performance in Your Web App: A Step-by-Step Guide
Source: github.blog

What You Need

  • Familiarity with JavaScript and modern web APIs (IndexedDB, Service Workers)
  • Access to your web app's codebase (or a demo project)
  • Basic understanding of performance metrics (TTFB, FCP, perceived latency)
  • A browser with developer tools (Chrome or Firefox)
  • Optional: a load-testing or analytics tool to measure improvements

Step 1: Identify the Bottleneck – Measure Perceived Latency

Before optimizing, you need to know where the pain is. Focus on perceived latency, not just raw network times. In GitHub Issues, the problem wasn't that each page was slow; it was that repeated navigation between the list and issue details felt sluggish due to redundant fetches.

  1. Track the time from user action (click) to content visible on screen.
  2. Log every network request made during a typical navigation flow.
  3. Separate “instant” from “wait”: any delay above 100ms becomes noticeable.
  4. Identify pages where the same data is fetched multiple times (e.g., from a list to a detail view and back).

Your metric: time-to-interactive after a navigation. The goal is to make subsequent navigations feel instant by using locally cached data.

Step 2: Build a Client-Side Caching Layer with IndexedDB

The core idea: render pages instantly from a local cache, then revalidate with fresh data from the server in the background. GitHub Issues used IndexedDB to store issue data client-side—fast, persistent, and capable of handling structured data.

  1. Choose an IndexedDB wrapper or use the native API. (Consider libraries like Dexie.js to simplify.)
  2. Design a schema that matches your data model. For Issues, store issue objects keyed by repository + issue number.
  3. On each successful API response, store the data in IndexedDB with a timestamp for staleness checks.
  4. On navigation, first check IndexedDB: if data exists and is reasonably fresh (e.g., less than 30 seconds old), render from cache immediately.
  5. In the background, fetch the latest data. If it differs, update the cache and re-render.

This pattern turns “wait for network → render” into “render instantly → update silently”. Users see content before the network even responds.

Step 3: Implement a Preheating Strategy to Boost Cache Hit Rates

A cache is only useful if it contains what the user needs next. Preheating predicts future navigations and loads data into IndexedDB before the user clicks. But you must avoid spamming the server with requests.

  1. Analyze common user flows: e.g., from issue list to detail view, or from issue to linked issue.
  2. When a user hovers over a link or scrolls near one, trigger a background fetch for that item’s data and store it in IndexedDB.
  3. Alternatively, after loading a list page, prefetch details for the top N visible items.
  4. Use a simple heuristic: if the user has visited a page type before, preheat other pages in the same session.
  5. Add a throttle to prevent excessive requests—e.g., preheat only when browser is idle, or limit to 2 concurrent prefetches.

Preheating dramatically increases the chance that a navigation finds its data already cached, making the transition seamless.

Step 4: Add a Service Worker for Hard Navigations

Even with a strong cache, hard navigations (user types a URL directly or reloads) can bypass the cache because the JavaScript runtime is fresh. A service worker intercepts these requests and serves cached data from IndexedDB, making hard navigations as fast as in-app ones.

How to Modernize Navigation Performance in Your Web App: A Step-by-Step Guide
Source: github.blog
  1. Register a service worker in your app's main script.
  2. In the fetch event, check if the request URL matches a pattern (e.g., /issues/...).
  3. If yes, open the IndexedDB cache and look for matching data. Return it as the response.
  4. If found, also fetch from network in the background to update the cache.
  5. If not found, fall back to network normally.
  6. Ensure the service worker can read IndexedDB (use the idb library or native calls).
  7. Handle errors gracefully: serve stale data if network fails, or show a loading state.

Now even a cold start (first visit or reload) feels fast because the service worker uses previously cached data.

Step 5: Test and Iterate – Measure, Refine, Repeat

The system isn't “set and forget.” You must monitor real-world usage and adjust.

  1. Collect metrics: cache hit rate, perceived load time, number of background revalidations.
  2. Compare before/after: use tools like Lighthouse, Web Vitals, or custom RUM to see improvements.
  3. Watch for trade-offs: higher memory usage, increased CPU for background tasks, possible stale data if TTL is too long.
  4. Tune parameters: expiration times, preheating thresholds, number of concurrent prefetches.
  5. Run A/B tests on a subset of users to validate changes.
  6. Deploy gradually: enable caching for power users first, then roll out widely.

GitHub Issues saw significant reductions in navigation latency using this approach. Your results will depend on your data size and user behavior.

Tips for Success

  • Start small: Pick one navigation path (e.g., list → detail) and optimize it fully before expanding.
  • Respect user's device resources: IndexedDB storage limits can be hit on mobile. Implement eviction policies (e.g., remove oldest entries).
  • Use stale-while-revalidate: This pattern (serve cache, then update) is a proven web pattern—adopt it as your default.
  • Monitor background revalidation: Ensure it doesn't conflict with user edits or cause data loss. Use request deduplication.
  • Educate your team: Client-side caching changes how you think about data freshness. Document the architecture.
  • Test offline scenarios: Service workers can make your app work partially offline—a huge UX win.
  • Remember the user's context switch: Every millisecond of saved latency preserves flow. That’s the real metric.

By following these steps, you can transform your web app from “good enough” to “feels instantaneous.” The patterns are transferable—apply them to any data-heavy application where navigation speed matters. Start with step 1 today.