Skip to content

Build your first feature

This guide shows you where your code goes and the 2-file edit that makes a feature paid. Build here any time; nothing before this step depends on it.

The popup renders a card titled “Your feature goes here”. That is apps/extension/components/YourFeature.tsx, a small commented component that is yours to gut:

  • Rename it freely (update the import in apps/extension/entrypoints/popup/main.tsx).
  • Replace its two buttons with your real UI. Keep the primitives from @extensionstart/ui and the token color scales; both themes come free.
  • Talk to the background only through sendMessage from @/utils/messaging. It is typed, so wrong payloads won’t compile.

Unlike the demos, YourFeature.tsx is core: the wizard never prunes it.

The default value-first preset raises the paywall on the 10th recorded action. Record one after your feature does its work:

sendMessage("gateAction", { name: "your-free-action" }).catch(console.error);

This is the free button in YourFeature.tsx, verbatim.

File 1: apps/extension/entrypoints/background/gates.ts. Add your feature ID to the list at the top:

export const PREMIUM_FEATURES = ["premium-demo", "export-pdf"];

File 2: your call site. Ask the gate engine before running the feature:

const decision = await sendMessage("gateFeature", { feature: "export-pdf" });
if (decision !== null) return; // the wall is already rendering; stop
// …run the premium feature…

The premium button in YourFeature.tsx does exactly this with the "premium-demo" ID, so you can compare against a working example.

  • The wall renders itself in the popup, sidepanel, and content-script surfaces; you never build wall UI.
  • Dismissals cool down. A dismissed paywall stays quiet for 24 hours. Every timing number lives in gates.ts.
  • Sign-in chains. Signed-out users see the sign-in wall first, then the paywall. Sign-in never fires standalone, which keeps you inside store policy.
  • Usage mirrors to the server. Events flush to POST /gate/events every minute; anything with stakes reads server counters, not client ones.
  • GateDemo (apps/extension/components/GateDemo.tsx): once your own feature calls gateFeature, delete the file and its two GateDemo lines in entrypoints/popup/main.tsx. Don’t prune the gate module to remove it; that would delete your paywall engine too.
  • Highlighter: the content-script demo is the content-demo module; rerun pnpm create extstart or see the module system.

Next: 5. Ship it. For presets, timing knobs, and manual wall control, see Paywalls.