Quota banner

Tells people they are close to or out of their limit, as a card or a slim strip above the composer.

Loading preview…

Install

  1. 1

    Run this command in your Tailwind CSS project. It adds components/ai/quota-banner-warning.tsx and installs its dependencies.

    $ npx shadcn@latest add https://gradientdeck.com/ai-components/r/quota-banner-warning.json

Usage & API

Example

quota-banner-warning.tsx
import { QuotaWarning } from "@/components/ai/quota-banner-warning"

<QuotaWarning
  percent={1}
  resetsAt="…"
/>

Props QuotaWarning

PropType & descriptionRequired
percentnumberRequired
periodstringOptional
resetsAtstringRequired
onDismiss() => voidOptional
onViewUsage() => voidOptional

Dependencies

  • React
  • Tailwind CSS
  • motion
  • lucide-react
Implementation notes

QuotaWarning requires percent and resetsAt. period, onDismiss, and onViewUsage are optional. The icon is a triangle alert in an amber tile, and a hairline amber gradient runs along the top edge. The card leaves padding on the right so the dismiss control does not cover the title. The fill is a sand-to-amber gradient, animated with scaleX unless reduced motion is set. The track hides overflow, so a percent past 100 clips the fill even though the number beside it does not clip. Both dismiss controls call the same callback. You do not get a separate event for the X. The demo does not pass onViewUsage, so "See usage" renders and does nothing until you wire it, which is the natural place to open Usage meter. There is no stored "already dismissed" flag inside the file. Hiding it is your state, the way the demo's open flag unmounts the card.

Choosing a layout

Show the warning once, when the percent crosses a line you choose, and let it be dismissed. Eighty percent is only the demo value. Show the reached card when further sends should stop, not as a toast that vanishes on its own. It has no timer for hiding itself. The countdown keeps displaying whatever seconds you pass, and the card stays until you unmount it. The inline strip is for the composer specifically, including while a few messages remain, because it stays in the line of sight above the field without covering the thread. The demo pairs it with a real field so you can watch remaining fall. That field is not part of the export.

Do not make a banner do the job of Usage meter. The ring and the weekly bars are for looking the allowance up. They have no upgrade button and no dismiss. Do not make the banner collect payment either. The reached card can point at a higher plan, but the amount, the new balance, and the pay button live on Credit top-up, for plans that sell credits after the included limit. Model unavailable is a different stop: one model is busy, the copy says nothing was charged, and the way through is a retry or a switch, not a plan reset. If both can happen, they need different sentences, because "out of messages" and "this model is busy" ask the person to do different things.

Which of the three belongs above the composer?

QuotaInline. The source comment calls it a slim notice that sits on top of the composer, and the classes match: rounded top, border with the bottom edge removed, one line of 13 pixel text. QuotaWarning is a card with its own shadow, a progress bar, and a dismiss control. QuotaReached is a taller alert with a clock row and two full-height buttons. Either card will fight the field if you try to glue it on. The inline export also has no dismiss, because it is meant to stay until remaining or resetsAt changes. Upgrade is a text button in the sand tone, not the filled bolt button used on the reached card.

Does the reached countdown know the wall-clock reset?

It only knows resetsIn in seconds, plus the resetsAt string printed in the sentence. The demo happens to use "3:40 PM" alongside a start count of 4,338 seconds, and nothing checks that those agree. When you pass zero, the digits read 0:00 and stay there. The card does not then enable a composer, and it does not flip into QuotaInline's "No messages left" copy. If you show both, you still set remaining to 0 yourself. "Tell me when it resets" will not read the clock either. It only toggles the notifying prop you control.