Skip to main content
These run in the site’s middleware to evaluate redirect rules, route experiment visitors to their variant page, negotiate a locale, read the visitor’s country, and give a first-time visitor a durable ID. Experiments are declared in the site’s experiments.json and passed to evaluateRouting.

routing/evaluate

@replohq/sdk/routing/evaluate

evaluateRouting

Routes a request in the site’s middleware.ts: redirect rules first, then experiments, then locale routing. Pass the site’s imported experiments.json through liveExperiments as experiments, so experiments start and stop without a publish; Replo’s publish generates the file from the site’s experiments. On a running experiment’s route, each visitor is bucketed by a hash of their replo_visitor_id cookie, so they see the same variant on every visit, and the request is rewritten to their variant’s page while the address bar keeps the shared URL. The assignment is recorded in the replo_experiments cookie for the analytics pixel and kept for the browser session, even if the rollout percentages change, and each counted visit sets a short-lived replo_experiment_exposure cookie that the pixel turns into a replo.experiment_exposure event. ?experiment=<experiment key>:<variant key> pins a variant for QA for the browser session; pinned visits are left out of results. A stopped experiment with a shipped variant sends every visitor to that page. A challenger page under /v/ is served with X-Robots-Tag: noindex. An invalid experiments.json is ignored, so every page renders as if no experiment existed. With an experimentConsent category, a visitor who hasn’t granted it in Replo’s consent banner sees the control and gets no experiment cookies.

routing/rules

@replohq/sdk/routing/rules

reploRedirect

Validates a redirect rule, returning it unchanged. Throws RedirectRuleValidationError carrying the first failing check’s message.
Also exported

routing/locale

@replohq/sdk/routing/locale

getLocaleFromPath

The configured locale a pathname’s first segment names, or null. Matching is case-insensitive — /en-ca/about still identifies en-CA — and the caller decides whether a non-canonical spelling deserves a redirect.

localeUrl

The URL of a logical path in another locale — the one primitive a language switcher and hreflang alternates need. Within the current host’s scope (or with no domains configured) it is the locale-prefixed path; a locale served by a different domain gets an absolute URL there, dropping the prefix when that domain serves only the one locale. Hidden-mode sites use the prefixed form too: the runtime converts an explicit locale prefix into a cookie plus a clean-URL redirect on arrival.
Overrides the visitor’s IP-derived country for testing geo behavior without a VPN — works identically in dev sandboxes and on the published site. At worst it lets a visitor pick which locale they see, which the locale cookie already allows deliberately.
Set by the runtime when a visitor explicitly picks a locale (a language switcher link, or landing on a locale-prefixed URL). Negotiation reads it ahead of any detection, so an explicit choice always wins.

reploLocaleRouting

Validates a locale routing config, returning it with every tag canonicalized and defaults applied. Throws LocaleRoutingValidationError carrying the first failing check’s message.
Also exported

routing/geo

@replohq/sdk/routing/geo

ipCountry

The visitor’s ISO 3166-1 alpha-2 country, or null when unknown. The override cookie wins so geo behavior is testable without a VPN — in dev sandboxes, where the Cloudflare header never exists, and on the published site alike. At worst the override lets a visitor pick which locale they see, which the locale cookie already allows deliberately.

routing/visitor-id

@replohq/sdk/routing/visitor-id

ensureVisitorId

Gives a first-time visitor a durable visitor ID (a UUID) in the cookieName cookie, so experiments bucket every page view of theirs the same way. Call it in middleware.ts on the response the rest of the middleware returns. It never replaces an existing cookie, so a visitor keeps any ID the customer’s own site already gave them, and it skips redirects. When the request’s host is cookieDomain or one of its subdomains, the cookie is scoped to cookieDomain so the customer’s other subdomains (their funnel or checkout) see the same ID. On any other host, such as a preview or a replosites.com address, or with cookieDomain: null, the cookie stays on the current host. With a requiredConsent category, the ID is only created once the visitor has granted that category in Replo’s consent banner. Pass null when the site needs no consent for it. Pass visitorId when the middleware already bucketed this request with a freshly generated ID, so the cookie stores the ID the response was built for.

findVisitorId

Reads the visitor ID for an experiment evaluation, or null when the visitor has none or hasn’t granted requiredConsent. Pass it the page’s await cookies() and the same cookieName and requiredConsent as ensureVisitorId, so a visitor who withdraws consent sees the default and logs no exposure.

routing/live-experiments

@replohq/sdk/routing/live-experiments

liveExperiments

Returns the site’s experiment routing as it is in Replo right now, so launching, ending or re-splitting an experiment reaches visitors without a publish. Pass the site’s imported experiments.json and hand the result to evaluateRouting as experiments. On a published site the routing is read from the site’s own KV store and kept in memory for 30 seconds; the first request after that rereads it, waiting up to 800ms before using the copy it has. The first request in a fresh worker waits the same way, and if the store can’t be read, no experiment runs and every visitor gets the original page. In local development and the Site Builder preview, which have no store, the imported file is used as is, so ?experiment= previews work. Either way, only pages listed in the file’s liveRoutes are ever served, so an experiment never sends a visitor to a page this build doesn’t have.