Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Case Quantity Number Input Form Block for React

A quantity field that sells in cases of six: typing a number snaps it up to a whole case on leaving, the keys and buttons move one case or five, and the line total is worked out in whole cents.

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

Sell by the case instead of by the bottle, with this number input form for React with Base UI. A compact header sits over a hairline frame with a plus mark on each corner, in two cells: a stepper (a Number Input with an outline Button on each side) and, behind a divider, the line total. A case is six bottles and an order is 6 to 120. Type any number and leaving the field snaps it up to the next whole case and says so ("Rounded up to 2 cases"); the arrow keys and the side buttons move one case, Page Up and Page Down five; past either end the field holds at it and the foot says why. The total reads "12 bottles in 2 cases × $27.00" over the amount, worked out in whole cents. Add to order adds the settled quantity, and an empty field puts focus back in it. It's built from Number Input, Field, Button, Shine Button and Guides, with a CSS entrance only. For wholesale ordering, packs, boxes and any quantity that moves in units.

FAQ

How do I change the case size, the limits and the price?

Edit the constants at the top: CASE (bottles in a case), MIN and MAX (the fewest and the most an order takes, in bottles) and CASE_CENTS (the price of a case in cents). settle is the rule for a typed number and stepFrom the rule for a button or key; both are pure functions, so the snapping, the notes and the total all follow from the same constants.

What happens when I type a quantity that is not a whole case?

Nothing happens while you type. Leaving the field rounds it up to the next whole case and says so in the foot ("Rounded up to 2 cases"); a number under 6 is raised to 6 and one over 120 is capped, each with its reason. The total under the divider follows the number as it will settle, so you see 12 bottles at once. Pressing Add to order with a number that still needs snapping settles it the same way and adds the settled quantity, which is the one the total already shows.

Which keys and buttons step the number, and is that the same everywhere?

Arrow Up and Arrow Down move one case and Page Up and Page Down five, in all four versions: the block takes those keys before the field sees them, so the library's own stepping never runs. The side buttons step one case and stay focusable at either end, where pressing one says the number is already at the limit. The field is a text box, since the stepping is the block's own.

How is the total worked out, and does it work with Arabic digits?

The price is held in whole cents (2700 a case), multiplied by the number of cases and formatted once with Intl.NumberFormat, so there is no floating point to round. A page in Arabic prints Arabic-Indic digits in the field on some versions; asciiDigits reads them back as ASCII before the number is parsed, and the amounts and the sentence keep their left-to-right order inside a right-to-left page.

What stays local, and how do I connect it for real?

Nothing is ordered: Add to order says so in the foot. To make it real, send the settled number of bottles from the submit handler, price it on your server (never trust the total on the page) and show the server's answer in the same status line. If stock or the case size depends on the customer, pass them in as props and change MAX and CASE_CENTS from them.

How do I connect it to react-hook-form or TanStack Form?

Store the settled number of bottles as the form's value and leave the typed text in the block, because the rounding happens on leaving the field. With react-hook-form, use Controller, call field.onChange with the settled number from the blur handler and keep settle as the rule in a resolver. With TanStack Form, validate with onBlur and set the value with field.handleChange after settle.

  • Pro

    A form panel with one required project-name field that shows the web address it will get as you type, flags a name that is taken or past 50 characters, and offers the next free address.

  • Pro

    Invoice Currency Select Form

    Checkout and address

    A grouped currency select with symbol, code and name, an amount field, and the same amount in three other currencies from typed sample rates, set with Intl.NumberFormat.

  • Pro

    A restaurant reservation in a card in card: a party size stepper, a date picker that closes Mondays and past days, five sittings as choices and a form that checks itself and confirms in place.

  • Pro

    Brand Colour Picker Form

    Numbers and pickers

    A brand colour field that makes a scale: a hex input and the colour picker kept in sync, eight named presets, and five derived steps by lightness, each with its hex and a copy button.

  • Pro

    Logo File Input Form

    Numbers and pickers

    A single-file field with rules read from the file itself: a square PNG or JPG, 1 MB or less, at least 256 by 256 pixels, shown as one row with its size, type and pixels, or refused with a named reason.

  • Pro

    One-Time Code Input Form

    Numbers and pickers

    A six-slot confirmation code in two groups of three with its behaviours in view: paste fills all six and checks at once, a wrong code is marked, counted and cleared, and a resend button waits 30 seconds.