@herazgo/varykit-vue
v0.3.2
Published
Vue 3 composable and component for VaryKit element-level multivariate testing.
Maintainers
Readme
@herazgo/varykit-vue
Vue 3 composables and components for VaryKit element-level multivariate testing.
This package works in any Vue 3 app (Vite, plain Vue Router, etc.) with no Nuxt present. It declares vue as a peer dependency and depends on @herazgo/varykit-core for bucketing. For Nuxt, use @herazgo/varykit-nuxt — it has the same API shape but resolves experiments from nuxt.config instead of an app-level provider.
The experiment model
Tests are organized as named experiments. Each experiment defines its own
segment universe and gets its own cookie (vary:<experiment>), so assignments
are independent: a visitor can be hero: b and pricing: control at the same
time. Configuration lives at app setup, provided by createVaryKit().
Install
pnpm add @herazgo/varykit-vueSetup
import { createApp } from 'vue'
import { createVaryKit } from '@herazgo/varykit-vue'
import App from './App.vue'
createApp(App)
.use(createVaryKit({
experiments: {
hero: {
segments: { a: 0.5, b: 0.5 },
endWith: 'b',
endsAt: '2026-10-14T00:00:00Z'
}
}
}))
.mount('#app')Usage
useVary(experiment)
Resolves the visitor's segment for the experiment, persisting it in the vary:<experiment> cookie so the assignment is sticky. Resolution happens once per app per experiment — every later useVary call and VaryShow/VaryHide instance sees the same segment.
<script setup>
import { useVary } from '@herazgo/varykit-vue'
const { segment } = useVary('hero')
</script>
<template>
<p v-if="segment === 'a'">A content</p>
<p v-else>B content</p>
</template><VaryShow>
Renders its default slot only when the visitor's segment for the experiment matches the segment prop.
<script setup>
import { VaryShow } from '@herazgo/varykit-vue'
</script>
<template>
<VaryShow experiment="hero" segment="a">
<p>A content</p>
</VaryShow>
<VaryShow experiment="hero" segment="b">
<p>B content</p>
</VaryShow>
</template><VaryHide>
The inverse of <VaryShow>: renders its default slot only when the visitor's segment does NOT match the segment prop.
<script setup>
import { VaryHide } from '@herazgo/varykit-vue'
</script>
<template>
<VaryHide experiment="hero" segment="a">
<p>Shown to everyone except segment a</p>
</VaryHide>
</template>Exposure events
The package emits an exposure event when a variant actually renders: a matching <VaryShow> / <VaryHide> slot. Register a handler once (e.g. next to createVaryKit):
import { onVaryExposure } from '@herazgo/varykit-vue'
onVaryExposure(({ experiment, segment }) => {
// e.g. $analytics.track('experiment_viewed', { experiment, segment })
})onVaryExposure returns an unregister function. No network calls, no dedup — wire your analytics tool and dedupe there.
Typed experiment and segment names
Augment the VaryExperiments interface once, and useVary returns a segment narrowed to that union:
// types/varykit.d.ts
declare module '@herazgo/varykit-vue' {
interface VaryExperiments {
hero: 'a' | 'b'
}
}const { segment } = useVary('hero') // segment: Ref<'a' | 'b'>Limitation: vue-tsc 2.x/3.x does not instantiate generic function components in templates, so VaryShow/VaryHide props are checked loosely in templates while useVary stays strict. (The Nuxt module gets full template strictness via generated types.)
Lifecycle
Experiments support startsAt (default: immediate), endsAt (default: forever) and endWith — the terminal segment everyone gets outside the window:
| Config | Behavior |
| ------ | -------- |
| no endWith | runs forever (until removed from config) |
| endWith only | pinned immediately at setup |
| endWith + endsAt | runs until the date, then pinned |
| endsAt without endWith | setup error |
The pin overrides existing sticky cookies and rewrites them on the next resolution, so "ended" means everyone — including previously assigned visitors — resolves to the declared segment. Exposure keeps firing with the pinned segment, so post-period conversions attribute cleanly. Lifecycle is evaluated at resolution time (once per app per experiment). Cookies are always persistent (400-day browser cap, refreshed on every resolution) — there are no cookie-duration knobs.
For content rendered via plain useVary branching (no VaryShow/VaryHide), emit manually when you decide the content was seen:
<script setup>
import { useVary, emitExposure } from '@herazgo/varykit-vue'
const { segment } = useVary('hero')
onMounted(() => {
emitExposure('hero', segment.value)
})
</script>For conversions, attach the segment to your own events.
Configuration
createVaryKit(options)
| Option | Type | Description |
| ------ | ---- | ----------- |
| experiments | Record<string, Experiment> | The experiment definitions, validated at setup. |
Each experiment supports:
| Option | Type | Description |
| ------ | ---- | ----------- |
| segments | Record<string, number> | Segment weights between 0 and 1. Omitted segments auto-split the remainder. |
| startsAt | string | ISO date; the experiment accepts traffic from this moment. Default: immediate. |
| endsAt | string | ISO date; the experiment stops splitting traffic at this moment. Requires endWith. |
| endWith | string | Terminal segment everyone gets outside the [startsAt, endsAt] window. |
Scope
This package owns Vue-specific persistence (document.cookie) and reactivity. It never imports from @herazgo/varykit-nuxt. It is client-side only — there is no SSR-safe story here; that is what the Nuxt module is for.
License
MIT
