Single-page applications
On a single-page app (React Router, Next.js, Vue Router and similar), the browser loads main.js once and your app then changes pages without a reload. Grona detects those route changes and re-runs its campaigns for each new page. This page states exactly what that covers, so you can tell whether your app fits before you build a campaign on it.
What Grona handles
Section titled “What Grona handles”Route-change detection. The runtime wraps history.pushState and history.replaceState, and listens for popstate. A route change counts only when the page address changes in its path or query string. Changes to the # fragment, UTM tags, ad-click ids and Grona’s own ?v=grona- parameter are ignored, using the same rules as campaign URL matching.
What happens on a route change:
- Grona waits 50 ms, so a burst of history calls counts as one change.
- It closes the page view for the previous route.
- It tears down the changes it applied to the previous route.
- It waits until the DOM has been quiet for 60 ms, or 1.5 seconds at most, so your app can render the new route.
- It matches campaigns against the new address and applies the first one that enrols the visitor, exactly as on a full page load.
Teardown. Inserted blocks are removed, and their clean-up callbacks run. Text, CSS, attribute, image and delete changes are undone, but only on elements that still show Grona’s value. Elements your app has already re-rendered are left alone.
Stale runs. If the route changes again while a variation is still downloading or applying, that run stops, and undoes what it had applied.
Late-rendering elements. When a change’s target element is not in the DOM yet, Grona watches the page and applies the change when the element appears. The watcher gives up after 20 seconds without progress.
React and Vue safety. A text change on an element that has child elements controlled by React or Vue is skipped and reported as failed. Replacing those children would crash your app on its next update.
Page views. Each campaign page in a single-page app is counted as its own page view, the same as on a site that reloads.
What Grona does not handle
Section titled “What Grona does not handle”- Hash routing. Routes in the fragment (
/#/products) are not supported. The fragment is ignored by URL matching, so hash routes cannot be targeted and do not trigger a re-run. - Re-renders on the same route. If your app redraws an element after Grona changed it, the change is lost for that page view. Grona does not re-apply it.
- Elements that appear late. The retry watcher stops after 20 seconds without progress. Elements rendered after that are not changed.
- Reorders. A change that moves elements is not undone on a route change, because their original position is not saved.
- Flicker on route changes. The page is hidden only on the first load. On a route change the original content can show for a moment before the changes apply. See Prevent flicker.
- Other frameworks. The text-change guard detects only React and Vue. Angular, Svelte, Solid, Lit and other frameworks are not protected from text changes that replace their nodes.
- Iframes and shadow DOM. Grona does not change elements inside iframes or inside shadow roots, including web components.
- A re-run API. There is no JavaScript call to make Grona re-run campaigns. It reacts to history changes only.
- Preview links. A preview link applies its variation to the page it opens and does not follow route changes. See Preview and QA a variation.