Migration React + Vite vers une PWA : comment j’ai modernisé un ancien projet web existant

Pour cette migration, je n’ai pas choisi de repartir d’un projet vierge. J’ai volontairement repris une ancienne application React + Vite déjà fonctionnelle, puis j’y ai intégré progressivement tous les éléments nécessaires pour la transformer en Progressive Web App. L’intérêt de cette démarche est simple : documenter une migration réaliste, sur une base existante, avec les contraintes techniques que l’on rencontre réellement en production.
Dans un projet Vite, la dimension PWA n’est pas fournie nativement de manière complète. Il faut donc ajouter une couche capable de générer le manifest, de produire le service worker, d’orchestrer le précache et de s’intégrer proprement au cycle de build. C’est précisément ce que j’ai mis en place ici avec vite-plugin-pwa.

1 Repartir d’une base existante, pas d’un exemple artificiel
Le projet existait déjà, avec son interface de jeu, sa structure de composants et sa logique front. Avant toute modification, je l’ai relancé localement pour valider un point essentiel : l’application devait fonctionner correctement avant la migration. C’est une étape trop souvent négligée. Pourtant, sans base saine, il devient difficile de distinguer ce qui relève du code historique et ce qui relève réellement de l’intégration PWA.
Cette approche permet aussi de produire une documentation crédible : on ne montre pas comment générer une PWA théorique, mais comment faire évoluer une application web réelle vers un niveau d’exécution plus moderne, plus installable et plus cohérent avec les usages actuels.

2 Pourquoi j’ai retenu vite-plugin-pwa dans un projet Vite
Dans un projet React + Vite, plusieurs stratégies sont possibles sur le papier : écrire un manifest.webmanifest à la main, coder un service-worker.js manuellement, gérer soi-même l’enregistrement ou s’appuyer sur un plugin spécialisé. J’ai retenu vite-plugin-pwa pour une raison d’architecture : dans Vite, la bonne intégration PWA doit rester alignée sur le pipeline de build.
- Génération centralisée du manifest
- Production du service worker au bon moment
- Précache cohérent avec les assets du build
- Gestion plus propre des mises à jour
- Intégration native avec la mécanique Vite
Une implémentation 100 % manuelle restait possible, mais elle aurait introduit plus de fragilité dans un projet existant : chemins à maintenir, versioning de cache à surveiller, risque de divergence entre les icônes réellement servies et les ressources déclarées. Dans ce contexte, le plugin n’est pas un raccourci “facile” ; c’est un choix de maintenabilité.

3 Installation du plugin et rôle dans l’architecture du build
L’intégration a commencé par l’installation de vite-plugin-pwa en dépendance de développement :
npm install vite-plugin-pwa --save-devLe choix de --save-dev est logique : cette dépendance intervient au niveau du tooling et du build, pas comme une bibliothèque runtime métier chargée directement par l’application. Son rôle est d’ajouter à Vite les briques techniques nécessaires à la PWA : génération du manifest, service worker, précache, mise à jour et enregistrement simplifié.
4 Création du dossier public et gestion propre des ressources statiques
Le projet initial ne disposait pas d’un dossier public. Je l’ai donc créé à la racine afin d’y placer les ressources statiques nécessaires à la PWA. Dans Vite, ce répertoire est le bon emplacement pour les fichiers qui doivent être servis tels quels dans le build final, sans transformation, sans renommage et sans hash imposé par le bundler.
Ce point est particulièrement important pour les icônes d’installation. Le manifest doit référencer des chemins publics stables. Stocker ces images dans src aurait exposé le projet à des variations de noms ou de placement dans le build, ce qui n’est pas souhaitable pour des assets système.

5 Ajout des icônes et cohérence des noms de fichiers
Le pack d’icônes ajouté dans public contenait les ressources suivantes :
android-chrome-192x192.pngandroid-chrome-512x512.pngapple-touch-icon.pngfavicon.icofavicon-16x16.pngfavicon-32x32.png
J’ai volontairement conservé les noms générés par l’outil de conversion d’icônes, puis aligné toute la configuration dessus. D’un point de vue technique, c’est plus propre que de renommer artificiellement les fichiers. La règle générale est simple : la configuration doit refléter les ressources réelles du projet, pas l’inverse.

6 Mise à jour de index.html : métadonnées, thème et icônes système
Le fichier index.html a été mis à jour pour refléter plus proprement l’identité applicative du projet et sa compatibilité mobile :
<!doctype html>
<html lang="fr">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="theme-color" content="#0f172a" />
<meta
name="description"
content="36 GAME est un jeu de réflexion basé sur un tableau numéroté, où le joueur doit atteindre 36 en appliquant des règles de déplacement simples mais stratégiques"
/>
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png" />
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png" />
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
<title>36 GAME</title>
</head>
</html>Le point important ici n’est pas seulement cosmétique. theme-color améliore la cohérence visuelle sur mobile, la description contribue à l’identité applicative du document, et apple-touch-icon reste utile pour iOS. En revanche, je n’ai pas relié manuellement un site.webmanifest dans le HTML, car le manifest est généré depuis vite.config.ts afin d’éviter toute duplication de responsabilité.
7 Enregistrement du service worker dans main.tsx
Le service worker généré par le plugin doit ensuite être enregistré côté client. Sans cette étape, il existe au build, mais il n’entre pas réellement en action dans le navigateur. L’enregistrement a été effectué directement dans main.tsx via l’API fournie par le plugin :

L’utilisation de virtual:pwa-register est cohérente avec l’architecture du plugin. On évite ainsi une intégration partiellement manuelle avec navigator.serviceWorker.register(...), moins propre ici. L’option immediate: true accélère l’activation pour les besoins de validation et de démonstration.
8 Centraliser la configuration PWA dans vite.config.ts
La source de vérité de la migration PWA a été centralisée dans vite.config.ts. C’est à cet endroit que la configuration du manifest, des assets inclus et du comportement d’enregistrement a été définie :
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { VitePWA } from 'vite-plugin-pwa';
export default defineConfig({
plugins: [
react(),
VitePWA({
registerType: 'autoUpdate',
includeAssets: [
'favicon.ico',
'favicon-16x16.png',
'favicon-32x32.png',
'apple-touch-icon.png'
],
manifest: {
name: '36 GAME',
short_name: '36 GAME',
description: 'Application web React avec Vite transformée en Progressive Web App',
theme_color: '#0f172a',
background_color: '#ffffff',
display: 'standalone',
start_url: '/',
scope: '/',
lang: 'fr',
icons: [
{
src: 'android-chrome-192x192.png',
sizes: '192x192',
type: 'image/png'
},
{
src: 'android-chrome-512x512.png',
sizes: '512x512',
type: 'image/png'
}
]
}
})
]
});Cette centralisation réduit les écarts potentiels entre les fichiers réellement présents dans public et les chemins utilisés par la PWA. Elle évite aussi la multiplication des points de configuration, ce qui améliore la lisibilité du projet à long terme.

9 La migration PWA ne suffit pas sans cohérence UI
Rendre une application installable ne suffit pas à produire une bonne expérience. La couche PWA agit sur l’installabilité, le runtime et la mise en cache, mais elle ne garantit ni lisibilité, ni ergonomie, ni qualité responsive. Dans ce projet, l’interface du jeu devait aussi rester exploitable sur mobile et sur desktop : lisibilité de la grille, taille des zones interactives, hiérarchie des boutons et stabilité visuelle globale.
Autrement dit, la réussite technique repose sur deux couches complémentaires : une couche PWA pour l’installation et le comportement applicatif, et une couche UI responsive pour l’exploitabilité réelle du produit.
10 Validation réelle : build, preview, manifest et service worker
L’un des points les plus importants à rappeler dans une migration PWA sous Vite est le suivant : la validation ne se fait pas uniquement avec npm run dev. Les artefacts PWA sont véritablement produits dans le build de production. C’est pourquoi la vérification finale doit passer par :
npm run build
npm run previewLa validation technique a ensuite été réalisée dans les DevTools du navigateur, en contrôlant l’onglet Application pour vérifier le manifest, l’état du service worker, les icônes, le start_url et la présence éventuelle du cache. C’est cette étape qui permet de confirmer que l’application est réellement reconnue comme installable.
11 Pourquoi je n’ai pas utilisé site.webmanifest comme source principale
Le pack d’icônes contenait aussi un fichier site.webmanifest. Techniquement, il aurait été possible de l’utiliser. Mais dans une architecture pilotée par vite-plugin-pwa, cela aurait créé un risque inutile de duplication de configuration. J’ai donc préféré conserver une seule source de vérité : vite.config.ts.
Ce choix est moins “magique”, mais beaucoup plus propre à maintenir. Toute la logique PWA reste regroupée au même endroit, au lieu d’être dispersée entre le HTML, le manifest statique et la configuration du build.
12 Ce que cette migration change réellement
Cette migration ne se résume pas à “ajouter un manifest et deux icônes”. Elle modernise la façon dont l’application est interprétée par le navigateur : reconnaissance comme application installable, prise en charge d’un service worker, présence de ressources déclaratives cohérentes, meilleure intégration mobile et base plus propre pour gérer les mises à jour et la continuité d’usage.
Elle permet aussi de documenter un vrai processus d’évolution technique : reprendre une base existante, auditer sa structure, ajouter la couche PWA sans casser le projet et conserver une architecture maintenable. C’est précisément ce qui rend une migration intéressante du point de vue ingénierie.
Aller plus loin : gérer l’état installé / non installé d’une PWA
Une fois la migration en place, l’étape suivante consiste à gérer proprement l’état d’installation côté interface : savoir si l’application est installée, masquer ou afficher certains boutons, et adapter l’expérience selon le contexte d’exécution.
Besoin d’une migration PWA sur une application web existante ?
J’accompagne les entreprises, startups et indépendants sur la migration d’applications web vers des architectures PWA modernes, avec une approche orientée code, performance, maintenabilité et expérience réelle sur mobile comme sur desktop.


