Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Savings Goal Dialog Block for React

A savings app's goals list with a deposit dialog shown open: a ring that moves when you choose what goes in, the figures under it, three quick deposits or a whole number of your own, a weekly amount and the day the goal is reached at that pace.

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

Let people watch a goal fill as they add to it, with this savings dialog for React with Base UI. It opens from the Savings page of Hoardwell, a savings app: the page's header holds an outline Add to goal Button, and the goals list, a bar for each goal, is drawn under a soft scrim inside a hairline frame with a plus mark on each corner. The dialog is built around a Circular Progress ring in foreground weights: the part already saved is solid, the part you are about to add is lighter, and the percent after the deposit sits in the middle, with the figures and one line under it ("$1,840 saved, adding $50. $1,110 to go."). A Toggle Group takes the deposit, +$25, +$50, +$100 or Custom, which turns on an Input for a whole number of dollars. A Segment Group picks the weekly amount, and one sentence says the day the goal is reached at that pace, worked out from a fixed date. Add to goal, the only Shine Button, puts the deposit into the goal and the drawn list shows the longer bar and the new amount. For preview the dialog is drawn open in place when the page loads; the header button opens the real Dialog, with the same ring and the same state. For savings apps, budgeting tools, fundraisers and any goal with a target.

FAQ

Why is the dialog already open, and what do I keep?

So you can see it without a click. On load the dialog is a copy drawn in place over the page: a native dialog element that isn't modal, so nothing is trapped, hidden or focused, and the page around the preview keeps working. The Add to goal button opens the real Dialog, modal, with its focus trap, Escape and focus return, and the copy stays closed until a reload. Both show the same DepositFields component and share the block's state, so a deposit chosen in the copy is there in the real one. In your app keep the Dialog and drop the copy: the "in-place" state, the scrim and the dialog element inside the frame.

How does the ring work, and what happens with reduced motion?

Two rings share one cell. The lower one is the lighter ring and takes the saved total plus the deposit, and it carries the percent in its middle; the upper one is solid and takes the saved total alone, so the difference between them is what you are about to add. Both are the catalog's Circular Progress, restyled to foreground weights so the Shine Button stays the only primary colour. Money is whole cents throughout (money prints it, percentOf rounds the share), and the rings move through the component's own stroke transition, which is switched off for people who ask for reduced motion: the figures and the percent change at once instead. The rings are decorative and hidden from assistive technology; the figures and the line under them say the same things in text.

What are the rules for the deposit?

A quick deposit is always good, unless it is more than what is still to go, which says so ("Only $20 to go: choose Custom for less."). Custom turns the number field on and moves focus into it; it must be a whole number of dollars from $1 up to what is still to go, checked when you press Add to goal, with its message in a slot under the row that is always there, so the dialog never changes height. Once a message shows it follows your choice and clears as soon as it is fixed. A met goal answers every deposit with "The goal is already met."

Where does the finish day come from?

From AS_OF, a fixed date the block treats as today, so the preview and its poster never age. The weekly amount is divided into what is left after the deposit, rounded up to whole weeks, and that many weeks are added with UTC date arithmetic (addDays), the first deposit a week from AS_OF. In your app replace AS_OF with today's date and pass the goal's own numbers. The sentence is the same text everywhere, and the date is wrapped so it stays in one piece in a right-to-left layout.

Does it move any money, and how do I wire it up?

No. Add to goal adds the deposit to the goal's saved total in local state, closes the dialog, says in a polite status message that no money moved, and the drawn list behind it shows the new bar and amount. In your app send the deposit to your server in add, take the amount from the response (the server owns the balance), and show a refusal in the same message slot with the dialog still open. The weekly choice here only feeds the finish sentence; to make it a real standing order, send it with the deposit.

Is it accessible from the keyboard?

Yes. The real dialog is named by its title and described by its sentence, focus starts at the deposit choices and stays inside until it closes, and Escape, Cancel and the close button all return focus to Add to goal. The trigger is passed to DialogTrigger through render, and the dialog is controlled with open and onOpenChange. The deposits and the weekly amounts are groups you move through with the arrow keys, the number field's message is tied to it with aria-describedby, and one polite status message reports the result. The copy shown on load moves no focus; Escape inside it, Cancel and its close button close it and put focus on the header button.

  • Pro

    Recurring Payment Dialog

    Payments and plans

    A rent page with its autopay dialog shown open: an amount, how often, a start day from a folded inline calendar, when it ends, and the next four payment dates worked out under the fields.

  • Pro

    Quick Transfer Dialog

    Payments and plans

    A payments app's account home with its send-money dialog shown open: five recent people in a scrolling row, an amount with quick chips, a note, the fee and balance after, and a review step before Send.

  • Pro

    Add Card Dialog

    Payments and plans

    A transit wallet's payment methods page with its add-card dialog shown open: a card drawn in greys that fills in as you type, a number that groups itself and names its network, checks on the number, expiry and code, and an Add card button that keeps only the last four digits.

  • Pro

    Add to Cart Dialog

    Payments and plans

    A stool shop's product page with its add-to-cart dialog shown open: two heights that redraw the stool and move the price, three finishes with a sentence each, a quantity that stops at the stock left, and a total that follows the choices.

  • Pro

    Apply Coupon Dialog

    Payments and plans

    A cookware shop's basket with its promo-code dialog shown open: one field that judges five typed codes with five different answers, applied codes kept as removable chips, and a summary that is worked out again after every change.

  • Pro

    Billing Address Dialog

    Payments and plans

    An invoice page with its billing-address dialog shown open: the form relabels, reorders and revalidates for the United States, the United Kingdom, Germany and Japan, a switch fills the company's address, and the address is printed the way an envelope would carry it.