<AdaptifProvider state> is what the model can see about the current user. Pass plain JSON: strings, numbers, booleans, arrays, and plain objects. Other values are dropped.
Give the state a type on defineAdaptif, and list the keys each question should see in uses. A question with no uses receives the whole state and the timeline. A question whose uses lists only state keys never receives the timeline. "timeline" is a uses entry, not a state field.
onlyIf runs synchronously against that state before the question is registered. While it returns false, nothing is asked and the component stays on its fallback.
An optional id is a second name for the same question. Use it in JSX, in refresh, and in ?adaptif.force=. The text key and the id are the same question. Rewording the text keeps the identity when id stays put.
// adaptif.config.ts
import { defineAdaptif } from "@adaptif/react/config";
export interface AppState {
currentPage: string;
cart: string[];
}
export const adaptif = defineAdaptif<AppState>()({
situations: {
"The user seems to be weighing similar products rather than browsing past them": {
id: "comparingProducts",
onlyIf: (state) => state.currentPage.startsWith("/products/"),
uses: ["currentPage", "cart", "timeline"],
confidence: "low",
},
},
});import { AdaptifProvider, When } from "./adaptif";
export function ProductBrowser({ currentPage, cart }: { currentPage: string; cart: string[] }) {
return (
<AdaptifProvider state={{ currentPage, cart }} endpoint="/api/adaptif">
<When situation="comparingProducts" fallback={null}>
Comparison
</When>
</AdaptifProvider>
);
}<When situation="typo"> is a compile error. A question missing from adaptif.config.ts is never asked: the server rejects the id and the component renders its fallback.
confidence ("low", "medium", "high") is how sure the model must be before the UI changes; higher is stricter. For a situation it sets the show and hide lines: 0.5 / 0.3, 0.6 / 0.4 (the default), or 0.7 / 0.5. Model probabilities aren't calibrated to your app, so treat these as starting points and set threshold per question once you've seen real values. threshold sets the show line exactly, and the hide line follows 0.2 below it. Three more settings tune how long content stays up. minDwell (default "8s") is the shortest time it stays shown or hidden before switching. sticky: true keeps it shown for as long as the component is mounted once it has appeared, even if later answers drop. cooldown (for example "1d") is how long a component stays hidden after the user dismisses it with useDismiss(), across reloads. Props on <When> override the config entry.
Next: Add intents with Match.