A mobile user in Tamale, Ghana opens your web app on a 2G connection. Your homepage fires off 4 MB of JavaScript, three web font files, and a hero video. The spinner turns. And turns. They close the tab.
That scenario is not an edge case — it is the default reality for hundreds of millions of users across West Africa, rural India, and Southeast Asia. If your product is built for global reach or for local African markets specifically, designing for low-bandwidth conditions is not an accessibility afterthought. It is a core engineering requirement.
This guide covers the concrete techniques that actually move the needle.
Why "Just Add Lazy Loading" Is Not Enough
Lazy loading images is table stakes. It helps, but it addresses only one symptom of a deeper architectural problem: most modern web apps are built on the assumption of a fast, stable connection.
The real issues are:
- Payload size — Uncompressed JavaScript bundles routinely exceed 2–5 MB.
- Render-blocking resources — Fonts, stylesheets, and synchronous scripts stall the browser's paint cycle.
- All-or-nothing data fetching — APIs that return entire records when the UI only needs three fields.
- No offline fallback — A failed network request produces a blank screen instead of cached content.
Fixing these requires a shift in design philosophy, not just a checklist of optimisations.
Technique 1: Skeleton Screens Over Spinners
A spinner communicates "something is happening." A skeleton screen communicates what is about to appear. That distinction matters enormously on slow connections where waits can stretch to 8–15 seconds.
Skeleton screens — the grey placeholder blocks that mirror your UI layout — reduce perceived wait time by giving users a spatial map of the incoming content. The brain fills in the gap. Anxiety drops.
Implementation tips:
- Build skeletons in pure CSS using
background: linear-gradientanimations. Avoid JavaScript-heavy skeleton libraries. - Match skeleton proportions to your real content blocks as precisely as possible.
- Swap skeletons out as data arrives incrementally, not all at once.
.skeleton-block {
background: linear-gradient(90deg, #e0e0e0 25%, #f0f0f0 50%, #e0e0e0 75%);
background-size: 200% 100%;
animation: shimmer 1.4s infinite;
border-radius: 4px;
height: 16px;
}
@keyframes shimmer {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
Technique 2: Progressive Enhancement as a First Principle
Progressive enhancement means building a functional baseline experience in plain HTML and CSS, then layering interactivity on top for capable devices and connections. It is the opposite of graceful degradation — you build up, not down.
In practice this means:
- Server-side render your critical path. If the first meaningful paint depends on a JavaScript bundle loading, you have already lost 2G users.
- Use
<noscript>fallbacks for key navigation and form actions. - Avoid JavaScript-only routing for content that should be universally accessible.
- Test with JavaScript disabled as a forcing function. If the page is completely broken, your baseline is too thin.
For SaaS dashboards, this often means delivering a read-only HTML view of the most critical data before your React or Vue bundle bootstraps. Users see numbers immediately. Interactivity arrives later.
Technique 3: Compressed and Adaptive Asset Strategies
Images
- Serve WebP or AVIF formats with JPEG/PNG fallbacks via
<picture>andsrcset. - Use responsive images with
sizesattributes so mobile devices never download desktop-scaled assets. - Set aggressive quality thresholds — 75% quality WebP is visually indistinguishable from 100% on a small screen.
Fonts
- Subset your fonts to include only the character ranges your app actually uses. A Latin-only subset of an icon font can be 90% smaller than the full package.
- Use
font-display: swapto prevent fonts from blocking text rendering. - Where possible, rely on system font stacks (
-apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif). They are zero-cost.
JavaScript
- Enable Brotli compression on your CDN or server. It outperforms gzip by 15–25% on text assets.
- Audit your bundle with tools like
webpack-bundle-analyzerand ruthlessly eliminate unused dependencies. - Code-split aggressively. Load only what the current route needs.
Technique 4: Delta Syncing Instead of Full Refetches
Most apps refetch entire API responses when only a fraction of the data has changed. On a 2G connection, a 200 KB JSON payload where 190 KB is unchanged is a waste that compounds every time the user navigates.
Delta syncing — sending only the diff between the client's last known state and the current server state — dramatically reduces data transfer.
Practical approaches:
- Use ETags and
If-None-Matchheaders to return304 Not Modifiedwhen data has not changed. This is free with most HTTP stacks. - Design API responses to support field selection (
?fields=id,name,status) so clients request only what they render. - For real-time data, prefer WebSocket or SSE with delta payloads over polling full records.
- Implement a local cache layer (IndexedDB or Cache API) with a versioned sync protocol so the app can resume from where it left off after a dropped connection.
Technique 5: Design for Intermittent Connectivity
2G networks do not just move data slowly — they drop entirely. Your UI must account for three connection states: online, offline, and the murky middle ground of a request that has been sent but not yet acknowledged.
UX patterns that help:
- Optimistic UI updates — Show the result of a user action immediately in the UI, then sync to the server in the background. Roll back gracefully on failure.
- Offline-first queuing — Queue mutations locally when offline and replay them when the connection returns. Libraries like
workboxmake this manageable with Service Workers. - Connection-aware UI hints — Detect network quality with the Network Information API (
navigator.connection.effectiveType) and surface a subtle banner when the user is on2gorslow-2g, signalling that some features may be slower.
What This Means for Your Project
If you are building a SaaS product, a fintech app, or any consumer-facing tool that targets African markets — or any market where connectivity is inconsistent — low-bandwidth UI design is a direct revenue concern. Every second of load time on a slow connection is a user who does not convert, a transaction that does not complete, a support ticket that gets filed. The techniques above — skeleton screens, progressive enhancement, compressed assets, delta syncing, and offline-first patterns — are not premature optimisation. They are the foundation of a product that works where your users actually are.
Build for the constraint first. The fast connection will take care of itself.





