dispatchRouteChanged
Fires the Salla SDK event route::changed with the current page data; the TanStack router already fires it after every navigation.
import { dispatchRouteChanged, RouteChangedEvent } from '@salla.sa/twilight-theme-engine/utils';In plain words
Scripts outside React (analytics, chat widgets, Salla apps) need to know when the shopper moves to another page, because a storefront changes pages without reloading. After every navigation the engine sends the SDK event route::changed with the page's data: slug, title, id and so on. Anything can listen with Salla.event.on('route::changed', …).
dispatchRouteChanged() is the function that sends it. A theme listens; it rarely needs to send.
Signature
function dispatchRouteChanged(): void
type RouteChangedEvent = PageContext;
// {
// slug: string;
// id?: string | number;
// title: string;
// url?: string;
// parent?: { id: string | number; name: string; url: string } | null;
// breadcrumbs?: { name: string; url: string }[];
// }Try it live
import { useEffect } from 'react';
import { getSallaSDK, type RouteChangedEvent } from '@salla.sa/twilight-theme-engine/utils';
// The engine fires route::changed after every navigation; a theme only listens.
export function PageViewTracker() {
useEffect(() => {
const salla = getSallaSDK();
if (!salla) return;
const onRouteChanged = (...args: unknown[]) => {
const page = args[0] as Partial<RouteChangedEvent>;
if (!page.slug) return; // {} until a route with page data has loaded
console.log('page view', page.slug, page.title);
};
salla.event.on('route::changed', onRouteChanged);
return () => salla.event.off('route::changed', onRouteChanged);
}, []);
return null;
}
Example
import { useEffect } from 'react';
import { getSallaSDK, type RouteChangedEvent } from '@salla.sa/twilight-theme-engine/utils';
// The engine fires route::changed after every navigation; a theme only listens.
export function PageViewTracker() {
useEffect(() => {
const salla = getSallaSDK();
if (!salla) return;
const onRouteChanged = (...args: unknown[]) => {
const page = args[0] as Partial<RouteChangedEvent>;
if (!page.slug) return;
console.log('page view', page.slug, page.title);
};
salla.event.on('route::changed', onRouteChanged);
return () => salla.event.off('route::changed', onRouteChanged);
}, []);
return null;
}
How it behaves
It reads
getTwilightContext().pageand callsSalla.event.dispatch('route::changed', page ?? {}). It does nothing on the server, or whileSalla.event.dispatchis missing.createRouter()from@salla.sa/twilight-theme-engine/tanstackcalls it fromrouter.subscribe('onResolved'), after the loaders have finished and the new location is in the context. According to the engine's comment in the same file,onResolvedfires only on later navigations, so the page the browser first opens is not announced this way; its page data reaches the SDK throughSalla.init()instead (src/providers/twilight-init.ts).The page data is the
pagefield of the deepest route's loader data, stored by the router'sonLoadsubscriber, which also writes it to the SDK config aspage.
Gotchas
Calling it yourself under TanStack sends a second event for the same navigation, so listeners such as page-view trackers count the page twice.
The payload is not always the current page. During client navigations the router's
onLoadsubscriber replaces the context'spageonly when the new route has page data, and never clears it, so a route without any (a 404, a custom page) sends the previous page again (src/tanstack/router.tsx). It is{}only when the page first opened had no page data and no page with data has loaded since. docs/24-route-change-events.md says 404 and error pages send{}.The type marks
slugandtitleas required, but the payload can be{}. Check before reading them.