Skip to content

[Website] — Offline-first browsing via Service Worker & cache #7665

Description

@indpriyanshuraj

Please confirm the following.

What parts of Modrinth is your feature request related too?

Modrinth.com website

Is your suggested feature related to a problem? Please describe.

Imagine you're browsing modrinth.com, your connection drops, you hit reload — and the page is gone. Not a cached copy, not a friendly message, just the browser's error page. Everything you were looking at five minutes ago might as well have never loaded.

That's the current reality — the site has no offline story at all:

  • No offline state. A dropped connection means a dead stop, even for pages the user just visited. No cached shell, no images, nothing.
  • No feedback. The user is never told they're offline; every interaction simply fails.
  • Expensive repeat visits. On slow, metered, or flaky mobile connections, every reload re-fetches the shell and each project icon from scratch — a long blank screen for assets the browser downloaded minutes ago and may have already evicted from its HTTP cache.

The root cause: modrinth.com ships no Service Worker, so the only caching in play is the standard HTTP/CDN cache which does not survive connectivity loss and offers no control over what the offline experience looks like.

Describe the solution you'd like

Add a Workbox-based Service Worker to the website frontend (apps/frontend, Nuxt 3), making modrinth.com offline-first: fully browsable read-only from cache for anything previously visited, with a clear offline indicator and graceful handling of anything that needs a connection.

How a request flows through the Service Worker:

flowchart TD
    req["SW intercepts request"] --> auth{"Authenticated or<br/>a mutation?"}
    auth -- "Yes" --> netonly["Network only;<br/>friendly offline error when offline"]
    auth -- "No" --> type{"Resource type"}
    type -- "Immutable shell" --> precache["Cache First<br/>(precache manifest)"]
    type -- "HTML navigations &<br/>CDN images" --> swr["Stale While Revalidate"]
    type -- "Search & public API GETs" --> online{"User online?"}
    online -- "Yes" --> nf["Network First,<br/>cache as fallback"]
    online -- "No" --> stale["Serve cached entry,<br/>regardless of age"]
Loading

1. Caching tiers

Tier Resources Strategy Why it's the right fit
Shell Hashed JS/CSS bundles, fonts, static logos, prerendered /offline route Precache, Cache First Truly immutable — invalidated automatically whenever the Service Worker version changes
Pages HTML navigations (homepage, project pages) Stale While Revalidate Instant loads from cache, quietly freshened in the background so content never goes stale
Images cdn.modrinth.com icons & galleries Stale While Revalidate + entry cap Instant display, self-correcting if an author replaces a project icon, and bounded storage
Data Search queries & public, unauthenticated API GETs Network First + offline-aware expiry Fresh whenever online; served from cache whenever offline

2. Offline-aware expiry

Search and API responses are stored with a timestamp, and expiry behaves differently depending on connectivity:

  • While online — an expired entry means "go to the network first, keep the cache as fallback", and the cached copy is refreshed on success.
  • While offline — an expired entry is served anyway. Being offline should never make cached data less available.

Note

Nothing is ever deleted purely because of age. Entries are evicted only by LRU / quota pressure, so an offline user keeps everything they've already seen.

3. A non-blocking offline banner

When the connection drops or requests simply start failing, since navigator.onLine famously lies,a slim bar appears above the page content: "You're offline — showing cached content."

  • It never blocks or overlays the page; the site stays fully interactive.
  • It dismisses itself the moment the connection returns.

4. Read-only, not locked-down

Offline, the user keeps browsing everything cached. Mutations (settings changes, uploads, follows, …) are network-only and fail with a friendly inline "you're offline , this needs a connection" message rather than greying out half the UI to achieve the same thing.

Caution

Authenticated pages (dashboard, settings, notifications) are never served from cache. Offline, they show the /offline page — never a stale authenticated view. Nothing carrying an Authorization header or Set-Cookie is ever stored.

5. Update lifecycle

Warning

An old cached shell references hashed chunks that no longer exist after a deploy — the user gets a white screen. This plan is built to avoid exactly that.

  • The precache manifest is tied to the build, so a new deploy means a new manifest.
  • Old caches are deleted on activation.
  • A newly installed Service Worker waits and shows an "update available — refresh" prompt instead of auto-activating mid-session.

Describe alternatives you've considered

Rely on existing HTTP/CDN caching only Helps repeat-visit latency, but provides nothing during connectivity loss and offers no offline UX

Additional context

What works offline, and what doesn't:

Available offline Still needs a connection
Previously loaded application shell & static assets Pages never visited before
Previously cached images (icons, galleries) Authentication / account data
Previously visited pages New releases & versions
Cached search & API results, regardless of their age Search for new queries
Browsing, reading, navigating Uploads and all other mutations

Important

Safety rules, in one place:

  • Only GET requests returning 200 are ever cached.
  • Nothing carrying an Authorization header or Set-Cookie is stored — authenticated API traffic is strictly network-only.
  • Runtime caches are entry-capped and evicted by LRU / quota pressure, never by time alone.
  • Large file downloads stay on normal browser/CDN caching — they never enter Service Worker storage.
Explicit non-goals (click to expand)
  • Indiscriminate caching of authenticated or user-specific responses.
  • Caching mutation requests.
  • Storing large downloads in Service Worker storage.
  • Replacing existing browser/CDN caching.

Tip

  1. The exact routes, Workbox config, and cache sizes can be settled during implementation against the real apps/frontend architecture — the table above is the contract, not the final config.
  2. Enable Navigation preload,as latest content are going to fetch from network.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions