Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Grace Period Countdown Billing Block for React

A failed renewal's grace period as a rail of four milestones (the failure, a reminder, exports only, the lock) with today marked, a live time-to-lock countdown with a Pause, and one request for three more days that re-dates the later milestones.

Framework
UI library

Preview

Preview width

Installation

Part of Pro
Pro unlocks the code and one-line install of every Pro button and text effect, plus the MCP for your AI editor.
Have Pro already? Sign in to open it.Opens here as soon as you're Pro.

Overview

Tell customers exactly what happens while a payment is overdue, with this grace period countdown section for React with Base UI. It shows the account of Ironweld, a CAD tool, whose $89.00 Studio renewal failed on Mon 12 Oct. A hairline frame with a plus mark on each corner holds a rail of four milestones, each with its day, its date and what it does in words: the failure itself, a reminder email to the billing contact, exports only, and the day the workspace locks. Each milestone is marked Done, Today or Ahead from one fixed date, never the visitor's clock. A side cell holds the time until the lock, ticking from a typed duration with a visible Pause, an outline Button that asks for three more days once (the later milestones move and the button reads "Extension used"), and the one Shine Button, Update payment method. A single warning icon marks the countdown. It's built from Badge, Button, Shine Button and Guides, with a CSS entrance only. For dunning pages, past-due notices and any subscription app.

FAQ

Where do the four milestones come from?

They are typed in MILESTONES as a day offset from the failed payment (FAILED_ON) with a title and one sentence about the effect. The date of each is worked out from the offset, and whether it is Done, Today or Ahead comes from comparing that date with AS_OF. In your app these come from your dunning rules, so change the offsets and the sentences to match what your product really does.

Does the countdown really run?

Yes, locally. It starts from a typed duration (the days to the lock minus the time already gone today, NOW_SECONDS), subtracts one second a second, stops at zero and has a visible Pause and Resume button. For people who ask for reduced motion it starts paused. The ticking numbers are hidden from screen readers; a fixed sentence ("3 days and 14 hours until the account locks") is read instead, so nothing announces every second. In your app start from the server's lock time and compute the remaining seconds from it.

What does Ask for 3 more days do?

It can be used once. It adds three days to every milestone that is still ahead (the exports-only day and the lock), adds the same time to the countdown, changes the heading's date, and the button then reads "Extension used" and answers further presses with a message. Nothing is sent; in your app it would create a request your team approves, so show it as pending until it is.

What stays local, and how do I wire the payment button?

Everything: the countdown, the extension and the status messages are local state, and Update payment method only says that it would open your provider's hosted fields. Never collect a card number in your own page. Link the button to your payment provider's update flow or your billing portal, then refresh the milestones from the server once the payment goes through.

Is it accessible?

The rail is an ordered list; today's milestone carries aria-current="date", and every milestone says Done, Today or Ahead in words next to its glyph. The countdown can be paused. A polite status message speaks the result of each button, and the primary button is never disabled.

How does it look on a phone?

The rail comes first, then the side cell with the countdown and the buttons as a second band. The primary button ends on the first phone screen and a half.