Skip to content
Sign in
BillingLimit paywallMenu

Limit paywallPro

The upgrade at the moment of the limit: the locked result dithered or blurred behind one recommended plan, one action, Maybe later and a ticking countdown to the reset. Upgrading dissolves the treatment in place.

ProFour generated images dithered behind one offer and a reset countdown. Upgrade and they resolve in place.

Installation

$ pnpm dlx shadcn@latest add @chatcn-pro/limit-paywall

Usage

tsx
import { LimitPaywall } from "@/components/ui/chatcn/pro/limit-paywall"

<LimitPaywall
  locked={!user.isPro}
  title="You have used today's 25 free images"
  plan={{ name: "Pro", price: "$20", period: "per month", features }}
  onUpgrade={openCheckout}
  resetsAt={limits.resetsAt}
  resetWhat="25 free images"
>
  <ImageGrid images={result} />
</LimitPaywall>

The dither treatment is an ordered dither in CSS, so it works on any content: images, charts or text. The result is blurred, a 4 x 4 Bayer tile of threshold grays sits over it, and a steep contrast filter snaps each pixel to ink or paper, following whatever the theme painted. Locked content is inert and hidden from assistive tech; the panel says what is behind it.

Maybe later folds the panel into a slim bar with the countdown and the upgrade, and the result stays locked. When resetsAt passes, the line says the free limit is back and onReset fires once.

Examples

Blur treatment

ProA finished research report behind a blur, for text results.

States

ProOpening checkout, checkout failed, limit already reset, after Maybe later, and unlocked.

Props

PropTypeNotes
childrenReactNodeThe locked result
lockedbooleanDefault true. Set false and the treatment dissolves
treatment"dither" | "blur"Default dither
cellSizenumberDither cell in px. Default 3
title, descriptionReactNodeWhat happened and what to do
plan{ name, price, period?, features? }One plan, three features at most
actionLabel, onUpgradestring, () => unknownReturn a promise for progress; reject for the error
dismissLabel, onDismiss, defaultDismissedMaybe later
resetsAt, resetWhat, onResetThe ticking reset line
status, errorControl the states yourself
minHeightnumberDefault 320
labelsPartial<LimitPaywallLabels>Every string the paywall shows or announces, for translation

Keyboard

KeyAction
TabUpgrade, then Maybe later. The locked content is skipped

Accessibility

  • The panel is a region labelled by the title. A status line announces that the result is locked.
  • In forced colors the treatment is replaced by hiding the content, so nothing leaks through.

Motion

  • The panel enters from 0.96 in 220 ms. Unlocking interpolates the blur, contrast and tile to nothing in 400 ms, a once per purchase moment where the result resolves out of the dots.
  • Reduced motion: 150 ms fades.