Upgrade prompt

Pro

An upgrade nudge for the moment someone hits a limit. It shows a usage meter at 50 of 50 AI credits, a rolling price with a monthly and yearly toggle, three concrete unlocks with real model marks, and Upgrade or Maybe later. Upgrading leads into a success state with the new limits, and the card never changes size between states.

Live · keyboard ready

You've used all 50 AI credits

Your free AI credits reset Nov 1. Upgrade to keep going without the wait.

Limit reached50 of 50
$16/ month

$192 billed yearly. Cancel anytime.

  • 2,000 AI credits a month
  • Claude Opus 5.5 and GPT-6.1 Sol
  • Priority queue with no daily caps

  • An AI product that hits a usage limit (credits, generations, seats) and needs a tasteful, conversion focused nudge.
  • A paywall moment inside an app, in a dialog, popover or inline in place of the blocked result.
  • Offering one next plan with a monthly and yearly price.
  • Comparing several plans side by side: use a pricing table or plan picker.
  • Buying prepaid credits without a plan change: use credit-topup.
  • Passive usage reporting on a billing page: use a usage meter.

Installation

Pro source and install commands unlock with a Pro plan.

example.tsx
import { UpgradePrompt } from "@/components/arc/upgrade-prompt/upgrade-prompt"; export function OutOfCredits({ workspace, onClose, retry }) {  return (    <UpgradePrompt      used={workspace.credits.used}      limit={workspace.credits.limit}      resetsOn="Nov 1"      onUpgrade={async (selection, signal) => {        const res = await fetch("/api/billing/upgrade", { method: "POST", body: JSON.stringify(selection), signal });        if (!res.ok) throw new Error((await res.json()).message ?? "Your card was declined. Try another card.");      }}      onUpgraded={() => workspace.refresh()}      onContinue={() => { onClose(); retry(); }}      onDismiss={() => { fetch("/api/nudges/upgrade/snooze", { method: "POST" }); onClose(); }}    />  );}

UpgradePrompt

The upgrade nudge shown the moment someone hits a limit: what they used, a usage meter, the price with a monthly and yearly toggle, what the next plan unlocks, Upgrade and Maybe later. Upgrading rolls into a success state with the new limits; Maybe later shows a calm reminder with a way back. With no props it shows a Free workspace at 50 of 50 AI credits and simulates the upgrade.

PropTypeDefaultDescription
usednumber50How much of the limit is used.
limitnumber50The current plan's limit. At or over it the meter reads Limit reached; below it, how many are left.
unitstring"AI credits"What is counted, plural, such as "AI credits", "seats" or "generations". Used in the title, meter and announcements.
resetsOnstring"Nov 1"When the current limit resets. Shown in the description and the Maybe later reminder.
planUpgradePlanPro, $20 a month or $16 a month yearly, 2,000 credits{ name, price: { monthly, yearly }, currency?, limit, features: { id, label }[], limits: { label, value }[] }. price.yearly is the per month price when billed yearly; the saving badge and the yearly charge derive from it. Feature labels and limit values take ReactNode, so inline marks work.
interval"monthly" | "yearly"–Billing interval, controlled.
defaultInterval"monthly" | "yearly""yearly"Billing interval on mount when `interval` is left out.
onIntervalChange(interval) => void–Called when the billing toggle changes.
onUpgrade(selection: UpgradeSelection, signal: AbortSignal) => Promise<void>–Starts the upgrade with { plan, interval, amount, currency }, where amount is charged today. Resolve when the plan is active; reject with an Error and its message shows under the price with Try again. Aborted on unmount. Leave it out to simulate a 1.1s upgrade.
onUpgraded(selection: UpgradeSelection) => void–Called once the upgrade succeeded, after the success state shows.
onDismiss() => void–Called by Maybe later. Record the snooze here; the card shows a reminder with See Pro again.
onContinue() => void–Called by Continue on the success state, usually to close the dialog and retry the blocked action. Leave it out and Continue resets the demo.
titleReactNode–Replaces the limit title, such as "You've used all 3 seats".
descriptionReactNode–Replaces the line under the title.
classNamestring–Extra class on the card.
Tab
Moves through the billing toggle, Maybe later and Upgrade, or Continue and See Pro again in the other states.
ArrowLeftorArrowRight
Switches between Monthly and Yearly on the billing toggle.
EnterorSpace
Activates the focused button.
  • The card is a labelled section ("Upgrade to Pro") with a heading, so it reads well inside a dialog or popover.
  • The usage bar is role="meter" with a value text such as "50 of 50 AI credits used".
  • Status is an icon plus a label (Limit reached, 1,950 AI credits left), never color alone.
  • A polite live region announces upgrading, success and errors. Without onUpgrade it says the upgrade is simulated and nothing was charged.
  • The rolling price is aria-hidden with a plain text copy for screen readers. Inactive states are inert and hidden, so focus never lands on a faded panel.
  • Upgrade stays focusable while processing (aria-busy) so keyboard focus is not lost.
  • The usage bar fills once when it scrolls into view and settles to the new limit after the upgrade on a 0.5s bounce-free spring, moved with transform so its rounded end never stretches.
  • The price rolls digit by digit between monthly and yearly on a 0.28s spring, in tabular numerals so nothing shifts.
  • Title, description, meter label and counts crossfade: 100ms out, 160ms in with a 2px rise. Panels fade the same way with a 4px rise.
  • Every variant and panel shares one grid cell, so the card never changes size between offer, processing, error, success and reminder, and the buttons stay in place.
  • Reduced motion: the bar and price jump to their values; crossfades become plain fades.
  • Fluid up to 440px. Below 420px viewport width padding drops to 20px.
  • At very narrow widths the billing toggle wraps under the price; feature lines stay single line.
  • Buttons sit side by side at every width: Maybe later at its natural width, Upgrade filling the rest.
  • No timers or loops while idle; the only timeout is the simulated upgrade.
  • The meter and price animate transforms only, through motion values, so there are no re-renders while they move.
  • Two inline SVG marks, about 4 KB.

Notes for AI

Give your coding assistant the Markdown reference instead of screenshots.

  • Show it at the moment of value: when an action is blocked by the limit, not on page load. Put it in a dialog or popover anchored to the blocked control.
  • Keep the three features concrete and specific to what the person was just doing (more credits, the better model, no waiting).
  • The price is display only. Charge the plan and interval on the server; amount in the selection is for confirmation copy.
  • Pass onContinue to close the prompt and retry the blocked action, so upgrading feels like it unblocked them.
  • Snooze the nudge in onDismiss so it does not reappear on every blocked action.

The full library index for assistants is at /llms.txt.