How to Build a Low-Bandwidth-First Web App with Next.js
Somewhere between a user tapping your app link and your homepage rendering, a 2G signal handoff happens. The spinner appears. Then nothing. They leave. This is not a hypothetical — it is the daily reality for a significant share of mobile users across sub-Saharan Africa, where average mobile download speeds on 3G networks routinely dip below 3 Mbps and latency spikes above 150 ms are common.
Most performance guides are written for teams optimising the last 200 milliseconds off an already-fast experience. This guide is for teams building the first usable experience on a constrained connection. Next.js gives you powerful primitives to do exactly that — if you know which levers to pull.
1. Treat Bundle Size as a Feature Requirement
Your JavaScript bundle is the first bottleneck. On a 3G connection with 3 Mbps throughput, a 500 KB compressed JS bundle takes roughly 1.3 seconds to download — before a single line of your code executes.
Next.js ships automatic code-splitting per page, but that default behaviour is only the floor. Go further:
- Dynamic imports with
next/dynamic— lazy-load any component that does not appear above the fold. Charts, modals, rich text editors, and map widgets are prime candidates. - Barrel file audits — importing one utility from a large library (
import { format } from 'date-fns') can silently pull in the entire package. Use the@next/bundle-analyzerplugin to visualise what is actually in your bundles, then switch to subpath imports or lighter alternatives. next/scriptwithstrategy="lazyOnload"— third-party scripts (analytics, chat widgets, social embeds) should never block your critical rendering path on a slow connection.
// Lazy-load a heavy chart component only when it enters the viewport
import dynamic from 'next/dynamic';
const RevenueChart = dynamic(() => import('../components/RevenueChart'), {
loading: () => <SkeletonCard />,
ssr: false,
});
The loading fallback here is intentional — which brings us to the next point.
2. Skeleton UIs Are Not Just Aesthetic — They Are Functional
A blank screen and a spinner communicate the same thing: uncertainty. A skeleton UI communicates structure and progress, which keeps users engaged while assets load. On slow networks, perceived performance matters as much as actual performance.
Build skeleton components that mirror the exact layout of your loaded content — same column widths, same card heights. Libraries like react-loading-skeleton make this straightforward, but a few CSS animate-pulse divs in Tailwind work just as well and add zero JavaScript weight.
Pair skeletons with Next.js's streaming support (available in the App Router via React Suspense boundaries) to progressively flush HTML to the browser. Critical content — the page shell, navigation, primary heading — renders immediately from the server while slower data-dependent sections stream in behind it.
3. Serve Images That Match the Network, Not the Design File
Images are typically the heaviest payload on any page. The next/image component handles responsive sizing and modern format conversion (WebP, AVIF) automatically, but there are additional configurations worth enabling for low-bandwidth contexts:
- Set
qualityto60–70rather than the default75. The visual difference is imperceptible at mobile screen sizes; the file size reduction is not. - Use
placeholder="blur"with a low-resolution base64 preview. This removes the jarring content shift when the full image loads. - For hero images, consider serving a CSS gradient or solid colour as the initial background and loading the actual image only after
DOMContentLoadedfires.
Also: compress all static assets aggressively. Enable compress: true in your next.config.js (it is on by default, but confirm it has not been disabled), and serve everything through a CDN with a point of presence in West Africa — AWS CloudFront's Lagos edge, Cloudflare's Accra and Nairobi nodes, or Bunny CDN's African coverage all reduce round-trip latency meaningfully compared to routing through European or US origins.
4. Build Offline-First with a Service Worker
Network interruptions on 3G/4G are not rare events — they are expected behaviour. An offline-first architecture treats connectivity as an enhancement rather than a requirement.
Next.js does not ship a service worker by default, but the next-pwa package (or the newer @ducanh2912/next-pwa fork) integrates Workbox into your build pipeline with minimal configuration. Once in place, define a caching strategy that matches your content type:
- App shell (navigation, fonts, global CSS): Cache-first. These assets change infrequently and should always be available.
- API responses / dynamic data: Stale-while-revalidate. Serve the cached version immediately, then update it in the background.
- User-generated uploads: Network-first with a cache fallback.
A properly configured service worker means that a user who loaded your dashboard yesterday can open it today with no signal and still see their last-known data — a qualitatively different experience from a blank error screen.
5. Minimise Render-Blocking at the HTTP Level
Two final configuration wins that live outside your component code:
Enable HTTP/2 or HTTP/3. Most modern hosting platforms (Vercel, Railway, Render) do this automatically. HTTP/2 multiplexing reduces the penalty of multiple concurrent requests — critical when a page needs to fetch several small API endpoints.
Set aggressive cache headers on static assets. Next.js already appends content hashes to static filenames, so you can safely serve them with Cache-Control: public, max-age=31536000, immutable. Returning users on slow connections skip the download entirely.
6. Test on the Connection Your Users Actually Have
Chrome DevTools' network throttling presets ("Slow 3G", "Fast 3G") are useful baselines, but they do not simulate packet loss or the latency spikes common on shared mobile towers. Use the network-throttle CLI tool or a physical device on a real SIM card for final validation. Build a Lighthouse CI gate in your deployment pipeline that fails the build if Time to Interactive exceeds your target threshold.
Why This Matters for Your Project
If you are building a SaaS product, a fintech platform, or a consumer app targeting African markets, bandwidth-first engineering is not an accessibility nice-to-have — it is a retention and conversion strategy. Every second of load time above two seconds correlates with measurable drop-off in emerging markets where mobile data costs relative to income are significantly higher than in Western benchmarks. The teams that win are the ones that treat network constraints as a design requirement from day one, not a performance debt to address post-launch.





