Deterministic sticky A/B variant assignment and deduped exposure tracking as a Convex component, with GDPR erasure and multi-tenant scoping built in.
npm install @vllnt/convex-experiments@vllnt/convex-experiments assigns subjects to weighted variants and records which variants they encounter. It supplies the assignment and exposure layer of an A/B test while your application measures outcomes.
Define weighted variants, start enrollment, and use logExposure to assign and record a subject. peek resolves a variant without writing, and results returns assignment and exposure tallies. Stopping an experiment preserves its recorded data. Scopes separate experiments across surfaces or tenants.
The host authenticates subjects, authorizes experiment management, and records conversions or quality scores. Exposure tallies are not conversion analysis or statistical significance. Use a new experiment key when changing an established allocation.
Maintained by [bntvllnt](https://bntvllnt.com) at [VLLNT](https://vllnt.com). Find the author on [GitHub](https://github.com/bntvllnt) and [X](https://x.com/bntvllnt).
The @vllnt/convex-experiments component integrates directly into your Convex app via defineApp. You call experiments.define to create an experiment with weighted variants, then call experiments.logExposure inside any mutation to enroll a subject and record the exposure in one step. The variant string is returned immediately and can gate behavior in the same function call.
The @vllnt/convex-experiments component derives the variant deterministically from a hash of the experiment salt and subjectRef, so the same subject always lands in the same bucket even before anything is stored. The peek method exposes this as a read-only query with no writes, which is useful for SSR or flicker-free first paint scenarios.
Calling logExposure deduplicates writes so each subject produces exactly one tallied row per experiment. The results method returns per-variant counts for assigned, subjects, exposures, and weight without scanning the raw exposure table, keeping reads efficient as the experiment scales.
The @vllnt/convex-experiments component accepts an optional scope parameter on every method. Passing a tenant or surface identifier folds it into the hash so bucketing is independent per namespace. All enrollment, assignment, and erasure operations respect the scope, letting a single component installation serve multiple tenants.
No. In @vllnt/convex-experiments, once a subject is assigned to a variant that assignment is immutable. The variants and salt fields are also locked after the first enrollment, and attempting to change them throws an error. To re-randomize a cohort you define a new experiment key.
The @vllnt/convex-experiments component exposes a forgetSubject mutation that deletes a single subject's assignment and exposure records for a given experiment key and optional scope. The deleteExperiment mutation performs a full cascade delete of all data for an experiment. Neither operation affects the per-variant result tallies for other subjects.
The @vllnt/convex-experiments component is auth-agnostic. The host application is responsible for resolving identity and deciding who may call define, setStatus, logExposure, or forgetSubject. The component receives an opaque subjectRef string and never inspects it, so you pass in whatever identifier your auth layer provides.
Yes. The component exposes a peek query that returns the deterministic variant for a subject without writing any data. The optional @vllnt/convex-experiments/react package provides a useVariant hook that wraps a host-re-exported peek query reference, enabling flicker-free first paint. The React package is tree-shakeable and is not pulled in for backend-only consumers.
The @vllnt/convex-experiments component requires convex@^1.41.0 as a peer dependency. It is registered by importing the convex.config entry point and calling app.use(experiments) inside your convex/convex.config.ts file, following the standard Convex component installation pattern.