# Use RightMessage in a single-page application

Install RightMessage once, let it observe client-side routes, and preserve visitor context through supported managed redirects.

Install RightMessage once in your application's shared page shell. The runtime observes client-side URL changes automatically, so React, Vue, Svelte, Angular, Next.js, and Nuxt do not need a second RightMessage router hook.

You need access to the application's source and the project's [production installation snippet](https://rightmessage.com/help/everything-you-need-to-know-about-your-rightmessage-tracking-script). [Publish the flows, widgets, and campaigns](https://rightmessage.com/help/publishing-your-changes) you want normal visitors to use. Installing the snippet does not publish draft configuration.

## Install the complete snippet once

1. Copy the project's generated production installation snippet.
2. Add it to the shared document head or your framework's persistent document layout.
3. Keep the complete generated snippet, including its startup configuration and cloak cleanup. Do not replace it with a guessed script URL.
4. Remove duplicate installations from route components, page-level effects, or a second tag-manager rule.

For a client-rendered application, use the HTML shell. For a server-rendered framework, use its supported global script mechanism.

Do not execute browser-only code during server rendering. Keep the snippet mounted across client-side navigation rather than removing and reinstalling it for every route.

The development snippet's `useDraftConfig` setting starts an authenticated draft-debugger session. It is not an anonymous staging mode or an analytics-off switch. Without a valid debugger token, RightMessage requests authorization, which can require signing in. Use an [authorized debugger session](https://rightmessage.com/help/using-the-rightmessage-debugger) to inspect a draft. The runtime falls back to published configuration when the draft request fails.

## Remove redundant route resets

If your application already calls `RM.push(['reset'])` from every router transition, remove that hook and retain the installation snippet.

RightMessage observes `history.pushState`, `history.replaceState`, browser back and forward through `popstate`, and hash changes. It also observes the Navigation API's `navigatesuccess` and `currententrychange` events when available. It updates route-dependent visual state and query context, then records a pageview after a 100-millisecond debounce.

RightMessage uses the full URL to decide whether to record a pageview. Repeated notifications for the same URL do not create another pageview. When URLs change rapidly, only the final route may produce a pageview.

Avoid replacing the History methods after RightMessage has installed its observers. If another library wraps them, preserve the existing implementation instead of discarding it.

## Reset only for state changes without a new URL

If your application changes state that RightMessage reads without changing the URL, request a reset after that state is available.

Illustrative call, not executed:

```javascript
window.RM = window.RM || [];
window.RM.push(['reset']);
```

The reset refreshes derived sources, widgets, and campaigns. It does not itself identify a logged-in user, publish a draft, or fetch every provider field. For a known email-platform contact, use the appropriate identification command in [Browser JavaScript commands and events](https://rightmessage.com/help/javascript-hooks).

Use `personalize` when you only need campaign reconciliation. Neither command is a replacement for your application's own rendering lifecycle.

## Use current or remembered query values deliberately

For a condition or template that should apply only to the current route, use `query.current.<name>`. To retain the most recent occurrence of a parameter as the visitor navigates, use `query.<name>`.

For example, a WorkForge visitor can arrive at `https://example.com/?utm_campaign=teams` and then navigate to `/pricing`. On `/pricing`, `query.current.utm_campaign` is absent, while `query.utm_campaign` can still be `teams` from stored page history. Empty strings and `'0'` count as present values.

See [Flow data and Liquid reference](https://rightmessage.com/help/flow-data-liquid-reference) for context paths and types.

## Carry context through RightMessage-managed redirects

To preserve context when a flow sends a visitor to another configured site or subdomain, use the flow's redirect or the offer's redirect handling. Install the appropriate RightMessage project snippet at the destination too.

For a synthetic WorkForge setup, the journey might start at `https://www.example.com/quiz` and finish at `https://app.example.com/recommendation`. RightMessage-managed redirect handling can add `_rm` to the destination URL. Do not build a separate encoder or manually copy a visitor's context into a reusable public link.

The carried context contains visitor and provider identifiers, recorded answers, identity scope, and a bounded set of campaign assignments. It is not a complete browser-storage export. The destination validates carried answer IDs against its configuration and reevaluates signal-based segmentation. Identical-looking segment names across different projects are not sufficient.

This mechanism does not rewrite every ordinary link on your website. A normal anchor, your own `window.location` assignment, or an unrelated router link is not automatically a managed redirect. Browser cookies also cannot span unrelated registrable domains.

At the destination, valid URL context takes precedence over `_rm_ctx` cookie fallback. After consuming URL context, RightMessage removes `_rm` with History rather than reloading the page. Query stripping by your own router or redirect service before the tag reads the URL can prevent this transfer.

### Account for cookie and payload limits

The `_rm_ctx` cookie normally lasts 365 days and targets an estimated registrable domain for subdomain continuity. It uses `SameSite=Lax`, `/` as its path, and `Secure` on HTTPS. Blacklisted hosts, including `localhost` by default, do not use this cookie. If the browser rejects the shared domain, the runtime falls back to a host-only cookie, which cannot provide cross-subdomain continuity.

The transport has a 3,500-character encoded budget. Campaign assignments use remaining capacity, with at most 1,200 additional encoded characters and 128 entries. A large base identity or answer payload is not guaranteed to fit a browser's cookie limit. The budget does not guarantee lossless transport of all data.

Provider identity scope restricts which configured project and connection can reuse an identifier. It is not encryption, a signature, or proof of identity. `_rm` and `_rm_ctx` are client-carried, decodable values. Never use them to authorize protected content, log someone into your application, or bypass server-side access checks.

Debugger and preview context do not establish normal visitor continuity. Do not use a debugger impersonation as proof that a production cross-domain journey has identified the visitor.

## Confirm the route behavior

After installation, inspect your own site with a test visitor:

1. Open a direct route and navigate to another route without reloading.
2. Use browser back and forward.
3. Check that widgets and personalization follow the destination's URL rules.
4. Compare a query-bearing route with a later route that omits that parameter.
5. If your flow uses a managed redirect, follow that actual flow and inspect the destination's visitor context.

If the wrong content remains, check the loaded snippet, the published page rules, the visitor's current segments, and browser console errors. Separate a route-detection problem from a campaign selector that no longer matches the framework's rendered HTML.

For synthetic visitor data, [use a valid `__rm_test` payload](https://rightmessage.com/help/simulate-visitor-data-with-rm-test-parameter). An invalid payload does not disable tracking, and analytics suppression is not a guarantee that custom JavaScript or provider writes are blocked.
