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/next, 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/next'
export default config({
site: 'routingcat-example',
publicKey: 'pk_routingcat-example_NcMQQWyZ', // public: names the site; write-only
objective: 'waitlist', // the main conversion: joining the waitlist, on the home and pricing pages
// tracked beside it: a paid first month, from Stripe's checkout webhook; "Talk to us" on /pricing,
// captured in PostHog and forwarded by a PostHog destination
outcomes: ['purchase', 'contact_sales'],
})
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 The Vercel project’s production address 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. Vercel’s build sends the project’s own instead.
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/next'
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' },
description: 'Lead with learning',
}),
shows: variant({
fields: { headline: 'Show each visitor the page that converts them' },
description: 'Lead with what each visitor sees',
}),
tries: variant({
fields: { headline: 'Try your ideas on real visitors, keep what works' },
description: 'Lead with trying ideas',
}),
},
}),
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.' },
description: 'The same promise, shorter',
}),
proof: variant({
fields: {
text: 'Every change is measured on your own visitors, so you see the proof before you keep it.',
},
description: 'Lead with measured proof',
}),
plain: variant({
fields: { text: 'RoutingCat picks the best version of your page for each visitor.' },
description: 'One plain sentence',
}),
},
}),
},
})
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 in Next.js’s notation: /, /pricing, /blog/[slug], /docs/[...slug], /[[...slug]].

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() or a component Required The variants, by key: at least one. A component alone stands for variant({ component }). 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, or a component.

Made of fields

Option Type Default What it does
fields object Required The content the page renders: JSON values, or images imported from your repository.

A component

Option Type Default What it does
component React component Required The component that renders the variant.
content object None What the component displays, declared as fields are, so that the variant’s versions and diffs show it.

Either kind

Option Type Default What it does
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 is made of fields or a component, never both.
  • 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.