Skip to content

Change, preview and deploy

Every change to what a page offers is a change to its slot markup in your templates. npx routingcat push stamps it and registers it, and your deploy serves it. This guide covers the changes you make most, how to look at a variant before and after it goes live, and how each deploy is registered. To change what is live without a deploy, see Change what’s live.

Try a new idea under a new key

Add a variant to the slot under a key the slot has never used: a new child of its container, carrying hidden. It goes live with your next production push, with a scorecard against its slot’s default: both are measured at the same time, on the same traffic. To compare two ideas, give each its own key. On Hobby, past the 10 live variants the account’s sites share, a new variant is held until there is room.

A key stays with its variant for good, even once the variant is retired, so a different idea always takes a new key. A variant your server honors, such as a price or a discount, is a trade-off on your site: see Weigh a variant your server honors before you add one.

Fix or refine under the same key

Edit a variant where it stands to fix a typo or tighten its wording. push stamps its new hash, which makes a new version, such as home/hero:learns@v2, and every version is kept.

  • Routing builds on what the variant already showed, rather than starting over.
  • Its scorecard compares the new version with the one it replaced, each over its own period, not at the same time. A different idea under an old key can’t be compared fairly, so it takes a new key.
  • A version is made when the variant’s own source changes, as your template writes it. Reformatting it makes none, its whitespace, quotes and attribute order, and neither does editing a file it includes, or moving hidden from one variant to another. Whitespace that shows, as between two inline elements, is part of it. See Pages, variants and versions.

Remove a variant, change the default, or bring one back

  • Delete a variant’s element from its slot, and the next production push retires it: it stops being served, and its history stays. Deleting a slot’s markup retires its variants; deleting a page’s slots and its entry in routingcat.config.json also stops routing it.
  • Write its key again, and the next production push brings it back with its history, and with a new version if its content changed.
  • Change the default before you delete it, since every slot needs one. Take hidden and data-nosnippet off the variant that becomes the default; put hidden on the old default and take out its style="display:contents", which would show it without RoutingCat’s script. push then gives the new default its style. Neither variant gets a new version, since a version leaves out what marks the variant that shows.

retire and resume do the same without a deploy. After either, change the template to match, so the next push agrees.

See a variant in development

On a host your config lists in no environment, such as your own machine, your pages run in development mode: they show the defaults and record nothing. A host listed under environments.preview, such as a staging server, records to the preview environment instead, out of your results. A push from another branch registers that preview’s markup through the GitHub link, or with the preview deploy key; a production deploy key is refused there. In development, add ?rc= to a page’s URL:

  • ?rc=hero:learns shows that variant in a slot of the page you open;
  • ?rc=home/hero:learns,subhead:short names the page and several of its slots;
  • ?rc=off returns to the defaults.

The choice lasts for the browser tab, and a badge in the page’s corner names what each slot shows. It works for any variant your head partial lists, so run push after each edit; your production hosts ignore it. In development, track and identify log to the browser’s console instead of recording.

Check what a push will make live

Terminal window
npx routingcat plan

plan reads your templates and routingcat.config.json as push does, checks them, and compares them with what production serves. It registers nothing. Try your first idea shows a full example.

  • Each variant has a mark: + new, ~ a new version, - retired.
  • Each line says what the variant will be compared with: its slot’s default, or its own previous version.
  • On Hobby, plan counts the site’s live variants, the defaults not counted, toward the 10 the account’s sites share, and marks each variant the push would hold.
  • plan home reports one page.
  • Exit code 0 means push will accept the change. Exit code 1 means it would refuse it, and the line says how to fix it.

Your agent runs plan and tells you what will go live before it registers.

Register each deploy

Your pages decide from what push registered, and each page names, in its head partial, the variants its deploy serves. So each production deploy is registered by push, run right after it, with push --check before it: a push before a deploy that then fails would make live variants your pages never ship. From a deploy that changed your templates until about 2 minutes after its push, your pages show your defaults and record no decision, and doctor names such a page. A site registers in one of three ways, which Set up RoutingCat sets up.

In CI: on GitHub Actions once npx routingcat link github has linked the site, in a job with permissions: id-token: write; or in any CI with the deploy key in ROUTINGCAT_DEPLOY_KEY. RoutingCat reads the branch from the CI’s own variables on GitHub Actions, GitLab, Bitbucket, CircleCI, Jenkins’ multibranch pipelines, Azure Pipelines, Buildkite, Cloudflare’s Workers Builds or Pages, Netlify and Vercel; in any other CI, set ROUTINGCAT_BRANCH to the branch the run deploys. The CI takes two steps:

  1. npx routingcat push --check before the deploy. It changes nothing, asks nothing of RoutingCat, and fails while a template lacks a stamp or the partial is out of date:

    $ routingcat push --check
    lumen · 1 template · 1 slot, 4 variants
    stamps 1 edit push would make
    layouts/index.html:3 home/hero:current data-rc-hash fe31485fd14a…, was 58c2042d15ca…
    partial layouts/partials/routingcat-head.html: up to date
    ✗ Your templates lack 1 of the edits routingcat push makes, so their slots would report other versions than this deploy registers. Run routingcat push, then commit what it changes. (D71, exit 1)
  2. npx routingcat push right after the deploy, which registers it:

    • with the GitHub link, production from a run of your production branch that a push, workflow_dispatch, schedule or repository_dispatch event started, and a preview from any other run, such as a pull request’s or a release’s;
    • with the production deploy key, production from a run of your production branch alone: a run of another branch, of a tag or of a pull request is refused. Such runs register previews with the preview key instead, from npx routingcat keys deploy --env preview.

The example app’s workflow on GitHub Actions, with the GitHub link, takes both steps around its build and its deploy, at the version its config pins, 1.0.3:

.github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
# `id-token: write` lets `push` ask GitHub for the run's OIDC token, which proves the push once
# `npx routingcat link github --branch main` has linked the site to this repository.
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- run: npm ci
- run: npx routingcat@1.0.3 push --check
- run: npm run build
# The site's own deploy of dist/ goes here.
- run: npx routingcat@1.0.3 push

In your host’s deploy script, for a host that deploys on its own, such as Forge, Ploi or a deploy hook on your server: push runs at the end of the script, once the server serves the new templates, with the deploy key and ROUTINGCAT_BRANCH in the script’s environment, as ROUTINGCAT_BRANCH=$FORGE_SITE_BRANCH npx routingcat push on Forge. push --check stays before the deploy, in your CI, or on your machine before you commit. Only a host without a deploy script needs your CI to start its deploy, through the host’s deploy hook, and push once the site serves it.

From your machine, for a site that pushes neither in a CI nor in its host’s deploy script: before you deploy, run push --check, and where it fails, push, which from your machine stamps your templates and registers nothing, then commit what it changes. Right after the deploy, run npx routingcat push --env production. It lists the edits it would still make, here on a Hugo site whose new hero slot was never stamped:

$ routingcat push --env production
lumen · 1 template · 1 slot, 4 variants
stamps 9 edits push would make
layouts/index.html:2 home/hero style="display:contents"
layouts/index.html:3 home/hero:current data-rc-hash 58c2042d15ca…
layouts/index.html:3 home/hero:current style="display:contents"
layouts/index.html:6 home/hero:oneclick data-rc-hash c4a2d99ee3bd…
layouts/index.html:6 home/hero:oneclick data-nosnippet
layouts/index.html:9 home/hero:studio data-rc-hash 975b5d620955…
layouts/index.html:9 home/hero:studio data-nosnippet
layouts/index.html:12 home/hero:best data-rc-hash c37fb91bad3a…
layouts/index.html:12 home/hero:best data-nosnippet
partial layouts/partials/routingcat-head.html: missing

Then it prints the plan, as plan shows it. At a terminal, it asks you to confirm. Run by your agent, it pushes nothing and ends:

✗ Nothing was pushed: this plan goes to production once the person confirms it. Show it to them; once they agree, run routingcat push --env production --confirm 6553f70fd916. (D115, exit 3)

Your agent shows you the plan, and once you agree, runs it again with --confirm and the digest. It pushes exactly the plan you saw, or nothing if your templates or production changed since:

written 9 stamp edits, layouts/partials/routingcat-head.html
routingcat LIVE home: 4 variants in hero, routing within about 2 minutes (ch_01m40j70srer495wr3vhnyvjca, pb_01m40j70srfb59gbvnmr3nv2t6)

Here the push also wrote the stamps the templates lacked, which the deployed pages don’t carry: commit them and deploy again, so your pages name what was registered. A push that has nothing left to write registers exactly what your deploy serves.

Know what each push makes live

Push What happens
Production: in CI or a deploy script on your production branch, or --env production It registers the change, which is live within about 2 minutes of both the push and the deploy.
A preview: in CI with the GitHub link on any other run, or with the preview deploy key It registers the new versions as a preview and changes nothing live.
On your machine, without --env production It stamps your templates and writes the partial, and registers nothing.

A production push applies only what its templates changed since the previous one, so an act on something your templates didn’t change holds. Its last line is RoutingCat’s receipt, one of these:

routingcat LIVE home: 4 variants in hero, routing within about 2 minutes (ch_2, pb_1)
routingcat NOT LIVE preview: 4 versions registered for home; production unchanged
routingcat registered ch_1 · objective signup · outcomes waitlist · no page routed yet
  • NOT LIVE also appears on a production push when routing is off for the site. On Hobby, until the site’s owner has a card on file, it reads routingcat NOT LIVE routing waits for a card on file: add one at …, nothing charged: see Add a card to start routing.
  • A push that can’t reach RoutingCat registers nothing and exits with code 4: run it again.
  • A push fails on an invalid declaration, with the same message plan would have shown; on a credential RoutingCat refuses, with its reason and exit code 1; and where npx can’t run the version your config pins, with exit code 2. A variant past the 10 live variants your Hobby account’s sites share is held instead, and the push names it after its receipt, with the held-variants warning.

Preview a variant in production

Terminal window
npx routingcat preview home/hero:oneclick

On a home page with one slot:

https://www.lumen.com/#rc_preview=1.eyJieSI6InByXzAxazZnIn0.c2ln
forces home/hero:oneclick@v2
path / (its first route) · on dp_01m4xns9ggfyhra1beeb6f424r · until 10:12Z
Opened in a browser, it shows a preview badge; the visit stays out of results and leaves your own assignments as they are.
  • The link is signed and valid for one hour. It shows the variants you name, at most one per slot of one page, and the defaults in the page’s other slots. Its token comes after #, so it never reaches your server, your logs or a referrer.
  • It opens the page’s first route without a dynamic segment, else its most frequent path over the past 7 days. Give --path /pricing when the page has neither.
  • The production deployment must carry the version. A push registers your deployment last, after its changes: if preview says no deployment that includes the version has registered yet, run the production push again on the same templates, then preview.
  • The badge names the variant each slot shows. A click hides it.
  • The preview lasts for the browser tab, until the link expires. Open the page with #rc_preview=off to end it sooner.

Next