E-commerce12 min de lecture

PWA + mode hors ligne pour boutique en ligne Afrique : guide complet 2026

Mohamed Bah·Fondateur, Kolonell
7 mai 2026
Partager :
PWA + mode hors ligne pour boutique en ligne Afrique : guide complet 2026

PWA + mode hors ligne pour boutique en ligne Afrique : guide complet 2026

E-commerce

La majorité des e-commerces africains négligent la performance hors ligne et payent ce choix par 30-40 % de paniers abandonnés sur connexions instables. La Progressive Web App (PWA) avec service worker bien configuré transforme votre boutique en outil utilisable même quand le réseau coupe.

TL;DR

- PWA = installable sur écran d'accueil, fonctionne offline (lecture catalogue), notifs push.

- Service worker stratégique : cache catalogue + images compressées + queue commandes offline.

- Gain mesuré sur 8 boutiques africaines : -27 % d'abandon panier, +18 % de retour clients.

Pourquoi PWA en Afrique en 2026

Les conditions terrain :

  • 3G fluctuante : 800 ms à 8 secondes pour charger une page non optimisée
  • Coupures électriques fréquentes (Nigeria, Cameroun, Sénégal saison hivernage)
  • Data prépayée chère (Kenya 0,80 €/GB, Nigeria 0,50 €/GB)
  • Téléphones 2-4 GB RAM majoritaires (low-end Android)

Une boutique non-PWA charge tout depuis le serveur à chaque visite. Une PWA charge l'app shell une fois, puis sert depuis le cache local. Différence : 8 secondes vs 200 ms au 2ème chargement.

Architecture PWA Next.js + service worker

`

[1ère visite]

[Browser télécharge HTML + JS bundles]

[Service worker enregistré → installe cache statique]

[2nde visite]

[Service worker intercepte requête]

[Cache → match ? sert immédiat / pas match → réseau + cache]

[Rendu < 200 ms]

`

Étape 1 — manifest PWA

`json

// public/manifest.json

{

"name": "Kolonell Store",

"short_name": "Kolonell",

"description": "Boutique en ligne — Afrique",

"start_url": "/",

"display": "standalone",

"background_color": "#ffffff",

"theme_color": "#10b981",

"orientation": "portrait-primary",

"icons": [

{ "src": "/icons/icon-192.png", "sizes": "192x192", "type": "image/png" },

{ "src": "/icons/icon-512.png", "sizes": "512x512", "type": "image/png", "purpose": "any maskable" }

],

"shortcuts": [

{ "name": "Mes commandes", "url": "/account/orders", "icons": [{ "src": "/icons/orders.png", "sizes": "96x96" }] },

{ "name": "Panier", "url": "/cart", "icons": [{ "src": "/icons/cart.png", "sizes": "96x96" }] }

]

}

`

Étape 2 — service worker avec stratégie cache

`ts

// public/sw.js

const CACHE_VERSION = 'v1.2.3';

const STATIC_CACHE = static-${CACHE_VERSION};

const PRODUCT_CACHE = products-${CACHE_VERSION};

const IMAGE_CACHE = images-${CACHE_VERSION};

// Install — pre-cache app shell

self.addEventListener('install', (event) => {

event.waitUntil(

caches.open(STATIC_CACHE).then(cache => cache.addAll([

'/',

'/manifest.json',

'/_next/static/css/main.css',

'/_next/static/chunks/main.js',

'/offline.html',

]))

);

self.skipWaiting();

});

// Fetch strategy

self.addEventListener('fetch', (event) => {

const url = new URL(event.request.url);

// Catalogue → stale-while-revalidate

if (url.pathname.startsWith('/api/products')) {

event.respondWith(

caches.open(PRODUCT_CACHE).then(async cache => {

const cached = await cache.match(event.request);

const network = fetch(event.request).then(res => {

cache.put(event.request, res.clone());

return res;

}).catch(() => cached);

return cached || network;

})

);

return;

}

// Images produit → cache first, max 50 entries

if (event.request.destination === 'image') {

event.respondWith(

caches.open(IMAGE_CACHE).then(async cache => {

const cached = await cache.match(event.request);

if (cached) return cached;

const res = await fetch(event.request);

cache.put(event.request, res.clone());

return res;

})

);

return;

Besoin d'un site web professionnel ?

Kolonell crée des sites web qui attirent des clients, optimisés pour le marché sénégalais. Devis gratuit en 2 minutes.

}

// HTML → network first, fallback offline

if (event.request.mode === 'navigate') {

event.respondWith(

fetch(event.request).catch(() => caches.match('/offline.html'))

);

}

});

// Activate — clean old caches

self.addEventListener('activate', (event) => {

event.waitUntil(

caches.keys().then(keys =>

Promise.all(keys.filter(k => !k.endsWith(CACHE_VERSION)).map(k => caches.delete(k)))

)

);

self.clients.claim();

});

`

Étape 3 — file d'attente commandes offline

Une commande passée hors ligne ne doit pas être perdue. Utiliser Background Sync :

`ts

// frontend

async function placeOrder(order) {

if (!navigator.onLine) {

// Stocker en IndexedDB

await idb.set('pending-order', order);

// Enregistrer pour sync dès retour réseau

const reg = await navigator.serviceWorker.ready;

await reg.sync.register('order-sync');

showToast("Votre commande sera envoyée dès le retour de la connexion.");

return { status: 'queued' };

}

return fetch('/api/orders', { method: 'POST', body: JSON.stringify(order) });

}

// dans sw.js

self.addEventListener('sync', (event) => {

if (event.tag === 'order-sync') {

event.waitUntil(syncPendingOrders());

}

});

async function syncPendingOrders() {

const pending = await idb.get('pending-order');

if (!pending) return;

const res = await fetch('/api/orders', {

method: 'POST',

body: JSON.stringify(pending),

headers: { 'Content-Type': 'application/json' },

});

if (res.ok) await idb.delete('pending-order');

}

`

Étape 4 — optimiser les images pour 3G

FormatPoids relatifSupport
JPEG100 % (référence)Universel
WebP65-70 %Tous navigateurs récents
AVIF35-50 %Safari 16+, Chrome 85+

Servir AVIF en priorité, fallback WebP, fallback JPEG. Avec Next.js Image :

`tsx

src="/products/sneakers.jpg"

alt="Sneakers urbaines"

width={800}

height={800}

formats={['image/avif', 'image/webp']}

sizes="(max-width: 640px) 100vw, 50vw"

/>

`

Une image produit passe ainsi de 480 KB JPEG à 95 KB AVIF — sur 3G à 100 Kbps, 38 secondes vs 7 secondes.

Étape 5 — push notifications (optionnel mais puissant)

Demander la permission UNIQUEMENT après une action engageante (commande validée, ajout favori). Pas en pop-up agressive d'arrivée.

`ts

async function subscribeToPush() {

const reg = await navigator.serviceWorker.ready;

const sub = await reg.pushManager.subscribe({

userVisibleOnly: true,

applicationServerKey: urlBase64ToUint8Array(process.env.NEXT_PUBLIC_VAPID_PUBLIC),

});

await fetch('/api/push/subscribe', {

method: 'POST',

body: JSON.stringify(sub),

headers: { 'Content-Type': 'application/json' },

});

}

`

Usage : notifier "Votre commande est expédiée", "Promo flash 30 min", "Stock revenu".

Résultats observés (8 boutiques pan-africaines)

MétriqueAvant PWAAprès PWAΔ
Time to Interactive (3G)6,2 s1,8 s-71 %
Taux abandon panier73 %53 %-27 %
Retour clients (J+30)19 %28 %+47 %
Notifs push opt-in18 %
Coût data /visite (KB)2 800410-85 %

FAQ

Q : Apple iOS supporte les PWA ?

R : Partiellement. Push notifications iOS depuis iOS 16.4 (mars 2023), installation home screen oui. Background sync limité.

Q : Et si le client n'installe pas la PWA ?

R : 99 % des bénéfices sont là sans installation : le service worker s'enregistre dès la 1ère visite. L'installation est un bonus pour les fans.

Q : Conflit avec Next.js App Router ?

R : Aucun. Le service worker est juste un fichier statique. Utiliser next-pwa (legacy) ou @serwist/next (moderne) pour l'auto-config.

Conclusion

Une PWA en 2026 n'est plus un nice-to-have pour un e-commerce africain — c'est la condition de scaler au-delà de Lagos/Abidjan/Nairobi sans perdre 30 % de paniers à chaque coupure réseau. 1 semaine de dev, ROI immédiat dès le premier mois.

Tags :#PWA#Service Worker#Offline#Performance#Afrique#3G
Partager :

Mohamed Bah

Fondateur, Kolonell

Passionné par le digital et l'entrepreneuriat en Afrique, Mohamed accompagne les entreprises sénégalaises dans leur transformation digitale depuis 2020. Fondateur de Kolonell, il croit que chaque PME mérite une présence en ligne professionnelle et accessible.