Skip to content

Config and declarations

routingcat.config.ts names your site and its conversions. Each page RoutingCat can vary is declared in its own file in routingcat/, with page(), slot() and variant(). All four come from routingcat/vue, and check what you declare when your code loads.

routingcat.config.ts

npx routingcat init writes it; you or your agent add the objective and the outcomes. From the example app:

routingcat.config.ts
import { config } from 'routingcat/vue'
export default config({
site: 'routingcat-nuxt-example',
publicKey: 'pk_routingcat-nuxt-example_Hq4Zr8Tw', // public: names the site; write-only
objective: 'waitlist', // the main conversion: joining the waitlist
outcomes: ['contact_sales'], // tracked beside it: "Talk to us" on the about page, from the browser
})
Option Type Default What it does
site string Required The site’s key, as npx routingcat init wrote it.
publicKey string Required The site’s public key, pk_ and the site’s key and 8 characters, as init wrote it. It names the site and only writes events, so it is safe in code.
origin string On Vercel, the project’s production address; elsewhere none The site’s production address, https:// and its host: https://www.example.com. Each production build sends it, so RoutingCat counts the site’s own domain and links previews to it. On Vercel, the build sends the project’s own instead; on Cloudflare, name it here, as npx routingcat init --origin writes it.
objective objective None: a routed page then needs its own The main conversion, which routing learns to get more of, on every page without its own. See Objectives.
outcomes string[] None Events tracked beside the objective, by name: counted and reported, never steering routing.

Objectives

An objective takes one of three forms, on the site or on a page. Its window is how long after a visitor first sees a page a conversion still counts for it: seconds, or a number then s, m, h or d. Limits and defaults gives its default and range.

Form Example
One event objective: 'signup'
Several, where any one counts objective: ['signup', 'trial_started']
With its window objective: { events: 'signup', window: '48h' }

An objective names at most 32 events. A page with two or more live variants needs one, its own or the site’s.

page()

page(key, options) declares a page: its key, its routes and its slots, in a file of routingcat/, one page per file. From the example app:

routingcat/home.ts
import { page, slot, variant } from 'routingcat/vue'
export const home = page('home', {
routes: ['/'],
slots: {
hero: slot({
default: 'current',
variants: {
current: variant({ fields: { headline: 'Route every visitor to what converts' } }),
learns: variant({ fields: { headline: 'Your pages learn what converts each visitor' } }),
shows: variant({ fields: { headline: 'Show each visitor the page that converts them' } }),
},
}),
subhead: slot({
default: 'current',
variants: {
current: variant({
fields: {
text: 'Each visitor sees the version of the page most likely to convert them, and you see which version works for whom.',
},
}),
short: variant({
fields: { text: 'Every visitor gets the page that works for them. You see what works.' },
}),
},
}),
},
})
Option Type Default What it does
routes string[] Required The paths the page serves, written as Routes says. At least one; a path belongs to one page.
objective objective The site’s The page’s own objective, in place of the site’s. See Objectives.
slots object: slot key → slot() Required The places on the page that can vary, by key.

Routes

Routes are written as Nuxt’s page files name them: /pricing for app/pages/pricing.vue, /blog/[slug] for app/pages/blog/[slug].vue, /docs/[...slug] for app/pages/docs/[...slug].vue.

slot()

slot(options) declares a place on the page that can vary. Slots vary independently, so every combination of their variants must render.

Option Type Default What it does
default string: a variant key Required The default: the variant the slot shows today, which the control always sees. One of variants.
variants object: variant key → variant() Required The variants, by key: at least one. The order they are listed in is kept.
renderer { key, schema? } None For variants made of fields: names what renders them, and the fields’ schema, the slot’s contract. Changing the schema changes the slot, never its variants’ versions.
fixedShare number 0 The share of the slot’s routed visits split evenly, or by weights, keeping each visitor on the same variant across visits: from 0 to 1. At 1, a classic A/B test.
weights object: variant key → number Even The fixed share’s split, by variant key: numbers from 0 to 1,000,000, relative to each other, one of them positive. A variant left out weighs 0 and stays out of the split.
personalization 'on' | 'off' On off routes every visitor of the slot the same way.
floor number Automatic The smallest chance each variant keeps of being picked for a routed visit outside the fixed share: from 0 to 1.
cap number Automatic The largest chance each variant can have of being picked for a routed visit outside the fixed share: from 0 to 1.
name string None A name for people and agents.
description string None What the slot is for.
tags string[] None Labels to organize slots.

The routing settings fixedShare, personalization, floor and cap can also change with npx routingcat set, without a deploy; a later deploy that changes one in code applies the code’s. weights change in code.

variant()

variant(options) declares one alternative for a slot, made of fields, which <RoutingCatSlot> renders.

Option Type Default What it does
fields object Required The content the page renders: JSON values, or images imported from your repository.
parent string | string[] None The variant this one was made from, by key, or a version of it, as quickstart@v3; several for a recombination.
relation 'fix' | 'refine' | 'fork' | 'recombine' None How it relates to its parent. Required with one parent; several read as recombine.
name string None A name for people and agents.
description string None What the variant tries.
tags string[] None Labels to organize variants.
learningGroups object: name → string None Groups you define, such as { angle: 'benefit-first' }, apart from tags. Every change is recorded; routing does not use them.

Rules

The functions refuse a declaration that breaks one of these when your code loads, naming its address.

  • Each function takes the options above alone: one it doesn’t take is refused, naming it and the option meant, as fixedShare for fixed_share.
  • A key, of the site, a page, a slot or a variant, is lowercase letters, digits, - and _, at most 40 characters.
  • A page lists at least one route, each once, each a path from /.
  • A route’s segments are each written as itself, or as a name in brackets alone: [slug] for one segment, and, last, [...slug] for the rest of the path or [[...slug]] for the rest or none. :slug and * are refused, with the brackets to write.
  • A slot’s default is one of its variants.
  • fixedShare, floor and cap are shares, from 0 to 1.
  • weights name the slot’s own variants, with numbers from 0 to 1,000,000.
  • One of the weights is positive: a variant they leave out weighs 0, outside the split.
  • A variant with one parent states its relation; several parents read as recombine.
  • config()’s publicKey is the public key of the site it names.
  • config()’s origin is an address alone, https:// and a host in lowercase, with no path.