Connection Quality Detection
What This Means
Connection quality detection allows your site to adapt to users' network conditions. Without it:
- Mobile users download unnecessary large assets
- Slow connections timeout on heavy resources
- Users on data-saver mode waste bandwidth
- Poor experience on unreliable networks
How to Diagnose
Network Information API
// Check for Network Information API support
const connection = navigator.connection ||
navigator.mozConnection ||
navigator.webkitConnection;
if (connection) {
console.log({
effectiveType: connection.effectiveType, // '4g', '3g', '2g', 'slow-2g'
downlink: connection.downlink, // Mbps
rtt: connection.rtt, // Round trip time in ms
saveData: connection.saveData, // Data saver enabled
type: connection.type // 'wifi', 'cellular', etc.
});
}
Connection Types
| effectiveType | Typical Use Case |
|---|---|
4g |
Full experience, high-res images |
3g |
Standard experience, optimized images |
2g |
Minimal experience, critical content only |
slow-2g |
Text-only or offline mode |
General Fixes
Adaptive Image Loading
// Load different image sizes based on connection
function getOptimizedImageUrl(baseUrl) {
const connection = navigator.connection;
if (!connection) {
return `${baseUrl}?quality=80`;
}
if (connection.saveData) {
return `${baseUrl}?quality=30&width=400`;
}
switch (connection.effectiveType) {
case 'slow-2g':
case '2g':
return `${baseUrl}?quality=30&width=400`;
case '3g':
return `${baseUrl}?quality=60&width=800`;
default:
return `${baseUrl}?quality=80&width=1200`;
}
}
// Usage
const imgUrl = getOptimizedImageUrl('/api/images/hero.jpg');
Conditional Feature Loading
// Load heavy features only on good connections
async function loadEnhancedFeatures() {
const connection = navigator.connection;
// Skip on slow connections or data saver
if (connection?.saveData ||
['slow-2g', '2g'].includes(connection?.effectiveType)) {
console.log('Skipping enhanced features due to slow connection');
return;
}
// Load video backgrounds, animations, etc.
await import('./enhanced-features.js');
}
// React with connection hook
function useConnectionQuality() {
const [quality, setQuality] = useState('unknown');
useEffect(() => {
const connection = navigator.connection;
if (!connection) {
setQuality('unknown');
return;
}
const updateQuality = () => {
setQuality(connection.effectiveType);
};
updateQuality();
connection.addEventListener('change', updateQuality);
return () => {
connection.removeEventListener('change', updateQuality);
};
}, []);
return quality;
}
Preload Strategy Based on Connection
// Aggressive preloading on fast connections only
function setupPreloading() {
const connection = navigator.connection;
if (!connection || connection.effectiveType !== '4g' || connection.saveData) {
return; // Don't preload on slow or metered connections
}
// Preload likely next pages
const linksToPreload = document.querySelectorAll('a[data-preload]');
linksToPreload.forEach(link => {
const prefetch = document.createElement('link');
prefetch.rel = 'prefetch';
prefetch.href = link.href;
document.head.appendChild(prefetch);
});
}
Connection Change Handler
// React to connection changes
function setupConnectionMonitoring() {
const connection = navigator.connection;
if (!connection) return;
connection.addEventListener('change', () => {
console.log('Connection changed:', {
type: connection.effectiveType,
downlink: connection.downlink,
rtt: connection.rtt
});
// Adjust behavior
if (connection.effectiveType === 'slow-2g') {
pauseVideoPlayback();
reduceImageQuality();
}
// Show indicator to user
if (connection.effectiveType === '2g' || connection.effectiveType === 'slow-2g') {
showSlowConnectionBanner();
}
});
}
CSS-Based Adaptation
/* Use low-quality images by default (save-data) */
.hero-image {
background-image: url('/images/hero-low.jpg');
}
/* Upgrade on fast connections via JS */
.hero-image.high-quality {
background-image: url('/images/hero-high.jpg');
}
/* Reduce animations on slow connections */
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}
// Apply high-quality class based on connection
if (navigator.connection?.effectiveType === '4g' && !navigator.connection?.saveData) {
document.querySelectorAll('.hero-image').forEach(el => {
el.classList.add('high-quality');
});
}
Offline Detection
// Handle offline state
function setupOfflineDetection() {
window.addEventListener('online', () => {
console.log('Back online');
retryFailedRequests();
hideOfflineBanner();
});
window.addEventListener('offline', () => {
console.log('Gone offline');
showOfflineBanner();
queueRequestsForLater();
});
// Initial check
if (!navigator.onLine) {
showOfflineBanner();
}
}
Browser Support
| Feature | Chrome | Firefox | Safari | Edge |
|---|---|---|---|---|
| Network Information API | Yes | Limited | No | Yes |
| navigator.onLine | Yes | Yes | Yes | Yes |
| connection.saveData | Yes | No | No | Yes |
Fallback strategy:
function getConnectionQuality() {
const connection = navigator.connection;
// Use Network Information API if available
if (connection?.effectiveType) {
return connection.effectiveType;
}
// Fallback: measure actual download speed
return measureConnectionSpeed();
}
async function measureConnectionSpeed() {
const startTime = performance.now();
const response = await fetch('/speed-test.bin'); // ~100KB file
const blob = await response.blob();
const duration = performance.now() - startTime;
const speedMbps = (blob.size * 8) / (duration * 1000);
if (speedMbps > 5) return '4g';
if (speedMbps > 1) return '3g';
if (speedMbps > 0.1) return '2g';
return 'slow-2g';
}
Verification
- Test with DevTools Network throttling
- Verify different assets load per connection
- Check saveData flag is respected
- Monitor connection change events