Navigation côté client
Passer d'une page à l'autre sans rechargement — clientSideNavigation remplace la zone de contenu au clic sur un lien de la barre latérale et garde à l'écran les données déjà chargées, et trackNavigation garde juste la marque de l'entrée courante après le remplacement.
Par défaut, chaque lien d'une app Sovrium est un chargement de page ordinaire : le navigateur récupère un nouveau document, et tout ce qui est sur la page — tableaux, tableaux kanban, leurs lignes déjà chargées — repart de zéro. C'est simple et toujours correct. Ces deux options échangent un peu de cette simplicité contre une navigation qui paraît instantanée.
clientSideNavigation
clientSideNavigation: true sur une sidebar transforme le clic sur un lien interne à l'app en remplacement du contenu. Sovrium ne récupère que l'élément <main id="main-content"> de la destination et remplace l'élément courant sur place ; le document lui-même n'est jamais rechargé.
pages:
- name: deals
path: /deals
components:
- type: sidebar
clientSideNavigation: true
groups:
- label: Sales
items:
- { label: Deals, href: /deals }
- { label: Pipeline, href: /pipeline }Déclare-la sur la barre latérale de chaque page qui doit y participer. Une page dont la barre latérale ne la définit pas se charge en entier quand on y arrive, et ses propres liens sont des chargements de page ordinaires.
Ce qui survit au remplacement. Le document, le runtime client et les données qu'il a déjà chargées. Un tableau ou un kanban déjà vu affiche ses lignes immédiatement au retour — sans squelette de chargement — et les rafraîchit en arrière-plan. Le titre de la page suit la destination, tout comme les boutons précédent et suivant du navigateur : chacun remplace à nouveau la zone que désigne la barre d'adresse.
La barre latérale vient avec le contenu. Elle se trouve dans <main id="main-content">, donc le serveur la rend à nouveau pour la destination, avec la bonne entrée portant aria-current="page". Tu n'as pas besoin de trackNavigation en plus.
Ce qui n'est jamais intercepté. Un clic avec une touche de modification, un clic du milieu, un lien avec target="_blank" ou download, un lien vers une autre origine, un lien vers /_admin, /api/ ou /assets/, un lien vers une ancre de la page courante, et tout lien portant l'attribut data-no-spa. Chacun se comporte exactement comme sans l'option. Utilise data-no-spa sur un lien qui doit toujours charger un document neuf.
Les pages toujours chargées en entier. Une destination qui déclare scripts, définit presence: true ou utilise un layout.sidebar lié à un enregistrement est chargée comme un document complet, parce que chacun de ces éléments vit en dehors de la zone remplacée. C'est aussi le cas quand la destination répond par une erreur ou une redirection (vers la connexion, par exemple), ou quand la page que l'on quitte a elle-même un layout.sidebar lié à un enregistrement ou presence: true. On arrive quand même à destination ; c'est simplement un chargement de page.
Les écouteurs continuent de fonctionner. Après chaque remplacement, sovrium:navigated est émis sur document — le même événement que celui décrit plus bas sous trackNavigation —, donc un script qui l'écoute voit les navigations côté client comme n'importe quelle autre.
Une différence connue. La barre latérale est rendue à nouveau avec chaque destination, donc sa propre position de défilement revient en haut à chaque navigation. Dans une longue barre latérale, on garde sa place dans le contenu, pas dans la liste.
trackNavigation
aria-current="page" est résolu sur le serveur, ce qui est juste et suffisant pour une app dont chaque navigation est un chargement de page. Une app qui remplace sa zone de contenu sur place laisse la barre latérale montée, et la marque du serveur figée sur la page que l'on a déjà quittée — l'élément qui répond à « où suis-je » devient alors celui qui se trompe.
trackNavigation: true recalcule la marque côté client après une navigation dans le même document. L'option est à activer explicitement parce qu'elle coûte un îlot client, et qu'une app qui ne fait que des chargements de page complets n'y gagne rien.
La marque se déplace sur deux signaux. popstate — les boutons précédent et suivant du navigateur — ne demande rien à ton app. L'autre est sovrium:navigated, l'événement qu'annonce un mécanisme de remplacement interne à l'app. Mets d'abord à jour window.location, puis émets un CustomEvent sur document :
history.pushState({}, '', '/products/widgets')
document.dispatchEvent(
new CustomEvent('sovrium:navigated', { detail: { path: '/products/widgets' } })
)Cet ordre est le contrat, pas une convention : la barre latérale lit elle-même window.location, donc detail.path n'est qu'informatif, et émettre l'événement avant la mise à jour de l'adresse recalcule la marque sur la page que l'on quitte.
activeMatch est réévalué côté client selon la même règle que côté serveur, donc une entrée prefix garde sa marque lors d'une navigation vers une sous-page. Un volet dont la section devient courante s'ouvre avec elle — mais n'est jamais rouvert après avoir été replié volontairement.
Dernière mise à jour 5 octobre 2026
Cette documentation a été rédigée avec de l'IA : des erreurs ou du contenu obsolète sont donc possibles. Sovrium est en bêta. Les contributions et corrections sont les bienvenues.