Skip to content

Latest commit

 

History

History
168 lines (117 loc) · 8.16 KB

README.md

File metadata and controls

168 lines (117 loc) · 8.16 KB

Adaptif

Adaptive React UI from a plain-English sentence. Describe a situation, and Adaptif shows the right UI when it's true for the user in front of you.

<When
  situation="The user is doing repetitive work that our Bulk Edit tool could automate"
  fallback={<StandardToolbar />}
>
  <BulkEditSuggestion />
</When>

You pass the app state you already have. Jev, TypeSafe AI's decision model, answers from your own server in about 100 ms to a second, and your UI follows. If the answer is slow, unsure, or anything fails, the fallback renders and your app behaves as if Adaptif weren't installed.

A person uses the app, a question is answered, and the UI changes

In a 72-case offline eval, Jev was right on 60 cases; the best hand-written rule I could write for each question got 45 (83% vs 63%, results and caveats). It's an early project, but the early results are promising. See what's next.

For a full example, see the candle shop.

When to use it

Use Adaptif when the right UI depends on what a user's behavior adds up to: the pages they visit, what they open, compare, undo, or come back to. A rule can count those events but can't read them, and a one-off LLM call doesn't follow the session or keep your UI from flickering. If an if statement can decide it, use an if statement.

  • Guided selling. A shopper opens the scent notes on candles from three different families and keeps going. Offer the scent quiz before they give up. "The shopper seems unsure which scent they would actually enjoy"
  • Price-aware carts. A shopper who sorts by price, removes an item, and reads the shipping page sees ways to save. One who reads reviews and picks the large size gets an upgrade offer instead. "How much is price weighing on this shopper's decision?"
  • Competitor migration. Show an import guide to trial users whose stated goal and first few pages suggest they're comparing you with a tool they already use. "The user is evaluating this product against a tool they already use"
  • Smart paywalls. Offer Pro only when the user's own goal needs a Pro feature, instead of nagging everyone. "The user's stated goal needs something only the Pro plan has"

The first two run in the candle shop.

How it works

  1. You register a question in adaptif.config.ts. That file is also your server's allowlist: anything not in it is rejected, so nobody can spend your API key on questions you didn't write.
  2. Your server asks the model. The browser sends the question's id and the state you pass to a route on your server. That route holds the API key and calls Jev.
  3. <When> shows the content or the fallback. If the answer is likely enough, the content renders. Otherwise the fallback does.

Nothing is read from the page: the model sees only the state and events you hand it. More in How it works.

Super Quick Start

The adaptif-setup skill reads your project, helps you write the first question, installs the packages, writes the config and the server route, keeps your current UI as the fallback, and checks the build. It works with Claude Code, Cursor, Codex, GitHub Copilot, and other agents, and never asks for your API key.

npx skills add robiscoding/adaptif --skill adaptif-setup

Then ask your agent: Set up Adaptif in this app. More in Agent skills.

Quick start

Show a gift banner when a shopper's browsing looks like gift shopping. No category rule can tell: a wallet might be for them or for their dad. The mix of what they look at says more. This uses Next.js; for other frameworks see Providers and frameworks.

1. Install. You need React 18.2 or 19 and Next.js 15.1+.

npm install @adaptif/react @adaptif/next

2. Register the question. The object key is the question text, and it's what <When situation> refers to. Put these next to your other top-level files, inside src/ if you have one. Both the browser and the server import the config, so keep secrets out of it.

// adaptif.config.ts
import { defineAdaptif } from "@adaptif/react/config";

export interface AppState {
  // Names of the products this shopper has viewed this visit, oldest first.
  viewedProducts: string[];
}

export const adaptif = defineAdaptif<AppState>()({
  situations: {
    "The shopper seems to be buying a gift for someone else": {
      uses: ["viewedProducts"],
    },
  },
});
// adaptif.ts
import { createAdaptifReact } from "@adaptif/react";
import { adaptif } from "./adaptif.config";

export const { AdaptifProvider, When } = createAdaptifReact(adaptif);

3. Add the route and a key. Get a key from the TypeSafe console and put TYPESAFE_API_KEY=… in .env.local, with no NEXT_PUBLIC_ prefix (other providers). The [...path] folder name matters: the browser calls /api/adaptif/evaluate. No key yet? Skip to Try it without a key.

// app/api/adaptif/[...path]/route.ts
import { createAdaptifRoute } from "@adaptif/next";
import { adaptif } from "@/adaptif.config";

export const { POST } = createAdaptifRoute({ config: adaptif });

The route reads the key on the first request, so next build works without it.

4. Show the banner. Until an answer arrives, and whenever it isn't likely enough or something fails, the fallback renders. fallback={null} leaves the page as it was.

// app/components/gift-banner.tsx
"use client";

import { AdaptifProvider, When } from "@/adaptif";

export function GiftBanner({ viewedProducts }: { viewedProducts: string[] }) {
  return (
    <AdaptifProvider state={{ viewedProducts }} endpoint="/api/adaptif">
      <When situation="The shopper seems to be buying a gift for someone else" fallback={null}>
        <p>Gift wrapping is free on every order this week.</p>
      </When>
    </AdaptifProvider>
  );
}

5. Try it. View a men's leather wallet, a kids' puzzle, and a lavender candle, and the banner shows up a second or two after the third. View the same running shoe in three colors and it stays hidden. The banner is asked again each time viewedProducts changes.

Using Mock

Mock is useful for testing, development, or if you just don't have a Jev API key yet.Pass mock instead of endpoint. true shows the child content, false hides it, and nothing is sent to the server:

<AdaptifProvider
  state={{ viewedProducts }}
  mock={{ "The shopper seems to be buying a gift for someone else": true }}
>

Docs

A few guides for diving deeper:

  1. Supply useful state
  2. Add intents with Match
  3. Improve timing with events and triggers
  4. Write good questions and tune thresholds

Going further:

Reference:

Contributing

Bug reports and PRs are welcome. CONTRIBUTING.md has the dev setup. If you find a security issue, please report it privately through the repo's Security tab instead of a public issue.

License

MIT