- La structure d’une URL ne se limite pas à une simple adresse : elle sert aussi de moyen pour stocker et restaurer l’état d’une application
- Présentation d’un cas comme la page de téléchargement de PrismJS, où une seule URL permet de reproduire entièrement les réglages de thème, de langage et de plugins
- Les différents composants comme le chemin, les paramètres de requête et le fragment expriment divers états, comme la navigation hiérarchique, le filtrage ou la navigation côté client
- Les filtres de recherche, la pagination, le mode d’affichage et la plage de dates se prêtent bien à une inclusion dans l’URL, contrairement aux informations sensibles ou à un état d’interface temporaire
- Une URL bien conçue améliore la partageabilité, la prévisibilité et l’efficacité du cache, et renforce la fiabilité de l’application web ainsi que l’expérience utilisateur
Le potentiel de l’URL
- L’URL n’est pas seulement l’adresse d’une ressource : elle fait aussi office d’interface utilisateur (UI) et de conteneur d’état
- Elle préserve automatiquement l’état pour le partage, les favoris, l’historique du navigateur et les deep links
- Depuis 1991, elle sert de mécanisme fondamental de gestion d’état sur le Web
- Chaque composant de l’URL exprime un type d’état différent
- Chemin (Path) : navigation hiérarchique dans les ressources (
/users/123/posts)
- Requête (Query) : filtres, options et réglages (
?theme=dark&lang=en)
- Fragment (Fragment) : position dans un document ou routage SPA (
#features, #/dashboard)
- La fonctionnalité Text Fragments permet de créer un lien direct vers un texte précis dans une page
Modèles de paramètres de requête
- Utiliser un délimiteur (delimiter) pour regrouper plusieurs valeurs sous une seule clé (
?tags=frontend,react,hooks)
- Sérialiser des données imbriquées en JSON ou en Base64 (
?config=eyJyaWNrIjoicm9sbCJ9==)
- Représenter les indicateurs booléens par la seule présence de la clé (
?mobile)
- La notation en crochets (bracket notation) permet de représenter plusieurs valeurs sous la forme
tags[]=frontend&tags[]=react
- Elle est reconnue automatiquement par
qs de Node ou certains middlewares Express, mais n’est pas standardisée
- L’essentiel est de rester cohérent
Exemples de représentation d’état via l’URL
- PrismJS : stocke l’ensemble des réglages de thème, de langage et de plugins dans le hash de l’URL
- GitHub : met en évidence une plage précise de lignes de code avec
#L108-L136
- Google Maps : inclut les coordonnées, le niveau de zoom et le type de carte dans l’URL
- Figma : partage le contexte de travail — position sur le canevas, zoom, éléments sélectionnés — via l’URL
- Sites e-commerce : intègrent les filtres, le tri et la plage de prix dans l’URL pour restaurer l’état de recherche
Modèles d’ingénierie frontend
- États qui se prêtent bien à l’URL
- terme de recherche, filtres, pagination et tri, mode d’affichage, plage de dates, onglet actif, configuration UI, feature flags
- États inadaptés à l’URL
- informations sensibles comme les mots de passe ou tokens, état UI temporaire, saisies non enregistrées, données volumineuses, états à très haute fréquence
- Critère de décision : si un autre utilisateur clique sur la même URL, doit-il voir le même état ?
Implémentation JavaScript
- L’API
URLSearchParams permet de lire et d’écrire les paramètres de requête
pushState ajoute une nouvelle entrée dans l’historique, replaceState met à jour l’entrée courante
- L’événement
popstate permet de restaurer l’UI lors d’un retour arrière dans le navigateur
Implémentation React
- Le hook
useSearchParams de React Router permet de gérer simplement l’état dans l’URL
- La lecture et la mise à jour des paramètres synchronisent automatiquement l’URL et l’UI
Bonnes pratiques de gestion d’état via l’URL
- Ne pas inclure les valeurs par défaut dans l’URL (ne conserver que
?theme=dark, et gérer les valeurs par défaut dans le code)
- Utiliser le debouncing pour éviter des mises à jour excessives de l’URL pendant la saisie (
lodash.debounce)
- pushState vs replaceState
pushState : pour les états réversibles comme un changement de filtre ou un changement de page
replaceState : pour les modifications fines comme la saisie dans un champ de recherche
Considérer l’URL comme un contrat
- Une URL bien conçue joue le rôle de contrat explicite entre l’application et l’utilisateur
- Elle clarifie les frontières entre public/privé, client/serveur et état partagé/session
- Une URL lisible exprime l’intention et reste compréhensible à la fois pour les humains et les machines
- Une forme comme
example.com/products/laptop?color=silver&sort=price transmet clairement le sens
- Amélioration de l’efficacité du cache
- Une même URL étant considérée comme une même ressource, le taux de hit du cache augmente
- Les paramètres de requête permettent aussi de contrôler les variantes de cache
- Versioning et expérimentation
?v=2, ?beta=true, ?experiment=new-ui permettent de distinguer version d’API et tests A/B
Anti-patterns à éviter
- Conserver l’état uniquement en mémoire dans une SPA, ce qui le fait disparaître au rechargement
- Inclure des informations sensibles dans l’URL (
?password=secret123)
- Utiliser des noms de paramètres peu explicites (
?foo=true&bar=2 au lieu de ?mobile=true&page=2)
- Encoder un JSON complexe en Base64, ce qui produit des URL excessivement longues
- Dépasser les limites de longueur d’URL (contraintes côté navigateur, serveur et CDN)
- Neutraliser le bouton retour (cas fréquent en abusant de
replaceState)
Conclusion
- Une bonne URL ne se contente pas de pointer vers un contenu : elle exprime une conversation entre l’utilisateur et l’application
- L’URL est l’un des moyens de gestion d’état les plus anciens et les plus élégants pour porter l’intention, le contexte et le caractère partageable
- Des outils complexes de gestion d’état comme Redux, MobX, Zustand ou Recoil existent, mais
ne pas oublier cette capacité native de l’URL reste l’une des vraies forces du Web
- Une application qui perd son état au rechargement passe à côté d’une propriété essentielle du Web
2 commentaires
Commentaires sur Hacker News
Lors des revues de code, j’essaie de stocker autant que possible l’état (state) dans l’URL
Après un rafraîchissement, être envoyé à un endroit complètement différent, ou voir une URL partagée afficher un écran sans rapport, est insultant du point de vue de l’utilisateur
Cette approche ralentit le développement, mais elle améliore la sensibilité UX au sein de l’équipe et permet de voir clairement combien d’état est encapsulé dans la vue
Certains craignent que l’URL devienne une sorte d’API publique, avec les contraintes que cela implique, mais je pense que ce n’est généralement pas un gros problème, car la plupart des URL ne sont utilisées qu’à court terme
Si nécessaire, on peut le résoudre avec du code qui migre les anciennes URL vers les nouvelles au chargement
Je pense que c’est un peu mieux avec des paramètres de requête plutôt qu’avec le chemin (path)
Pour l’utilisateur, le mot « retour » est naturellement associé au bouton du navigateur, donc c’est déroutant
Qu’un rafraîchissement réinitialise l’état est moins agaçant. Il y a l’idée que « rafraîchir = repartir de zéro »
Quand tout est géré en JS, ce genre de fonctionnalités de base se dégrade subtilement
Pourtant, même après avoir travaillé avec plus de 30 designers UX, je n’ai jamais reçu de consignes sur les URL
En particulier sur mobile, il est difficile de remettre une page dans son état initial, donc le rafraîchissement devient la solution la plus rapide
Avec l’infinite scroll ou des interfaces de filtres complexes, plus il y a d’état dans l’URL, plus la réinitialisation devient pénible
Si l’UX pose déjà problème et qu’en plus il faut remettre de l’ordre dans l’URL, cela ajoute un stress supplémentaire pour l’utilisateur
J’ai l’impression que même les personnes ayant une forte culture numérique comprennent mal les URL et le DNS
Elles devraient être capables de réduire les risques de phishing, de comprendre la signification des paramètres d’URL (
?t=_,utm_) et de retirer les informations personnelles avant de partagerIl faut aussi comprendre que le cadenas HTTPS ne signifie pas « confiance »
Utiliser l’URL comme conteneur d’état expose la structure interne et impose de gérer les versions
Cela peut aussi poser problème pour la compatibilité entre navigateurs ou les flux d’authentification
Malgré cela, j’essaie d’exposer autant d’état que possible dans l’URL, un peu comme des arguments de ligne de commande
Mais c’est un trade-off assumé, pas le résultat de l’ignorance ou d’un manque d’expérience
C’est une vieille bibliothèque mais elle reste utile : Rison
Elle permet de stocker proprement du JSON dans une URL, et elle est aussi utilisée dans Kibana d’Elastic
Exemple : http://example.com/service?query=q:'*',start:10,count:10
Quand un système évolue, la structure de l’état change aussi ; mettre l’état dans l’URL contraint donc cette évolution
Parce qu’une URL est fondamentalement une chaîne pérenne
À la place, je pense qu’il vaut mieux considérer l’URL comme une sorte de protocole, avec un mécanisme d’encodage et de décodage de l’état
Pour une page simple, il est possible d’y mettre l’état complet
Mais pour un flux, cela dépend des attentes de l’utilisateur, par exemple : « un rafraîchissement doit-il revenir à l’état le plus récent ? »
Les limites de longueur des URL varient selon le navigateur, le serveur, le CDN ou le moteur de recherche, mais on parle généralement de moins de 2000 caractères
Cela amène à se demander combien d’état on peut réellement y faire tenir, ou s’il faut une autre approche
- . _ ~), donc la densité d’information reste assez élevéedraw.io peut stocker l’état complet dans l’URL afin de le partager
Les données du diagramme sont encodées en Base64, ce qui permet une restauration complète via un seul lien
En revanche, je ne suis pas certain que cela corresponde vraiment à la définition d’un « state container »
Dans mes apps auto-hébergées, j’utilise le hash routing (#/dashboard)
Cela évite d’avoir besoin de réécriture d’URL côté serveur (.htaccess, etc.), donc même si ce n’est pas parfait, ça réduit les contraintes de déploiement
La version récente de Microsoft Teams gère tous les écrans avec une seule URL, donc il est impossible de les mettre en favoris
On ne peut pas ouvrir directement une équipe ou un canal précis, ce qui est très pénible
HATEOAS souffre d’un nom peu engageant, donc le concept n’attire pas l’attention, alors qu’au fond c’est une idée de base du web
Mais dans un environnement où l’on contrôle à la fois le serveur et le client, cela n’apporte qu’une complexité supplémentaire
Surtout si le client doit malgré tout connaître la structure des endpoints : dans ce cas, on ne fait que rendre les URL opaques
J’utilise souvent la mise en veille des onglets, mais avec les web apps qui figent l’URL et fonctionnent comme un seul bloc, les informations disparaissent dès qu’elles passent en veille.
Mais en même temps, ces pages web sont toutes tellement lourdes qu’on ne peut pas non plus se permettre de désactiver la mise en veille.