Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Unit Converter Dialog Block for Radix UI

A recipe app's unit converter shown open: four tabs of two linked number fields with unit pickers, a swap button, preset chips, the factor stated in a line and results rounded honestly to four significant figures.

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 convert a unit without leaving the page they are cooking from, with this unit converter dialog for React with Radix UI. It opens from the Pancakes recipe of Whiskwren, a recipe app: the page's header holds an outline Convert units Button, and the recipe card, a title bar and six ingredient lines, four of them real text, is drawn under a soft scrim inside a hairline frame with a plus mark on each corner. The dialog has four Tabs, Length, Weight, Temperature and Volume, and in each a From row and a To row, a Number Input and a unit picker on each, that edit each other: type in either number and the other follows. A Swap button turns the conversion round, three preset chips fill both rows ("1 cup to ml", "350 °F to °C", "1 lb to kg"), and a line states the factor, "1 cup = 236.6 ml", or the formula for temperature. Results are rounded to four significant figures and say so. Use 236.6 ml, the only Shine Button, writes the value into the recipe's line for that tab. For preview the dialog is drawn open in place when the page loads; the header button opens the real Dialog, with the same tabs and numbers. For recipes, shops, workshops and anything measured in more than one system.

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 recipe: a native dialog element that isn't modal, so nothing is trapped, hidden or focused, and the page around the preview keeps working. The Convert units 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 ConverterFields component and share the block's state, so a number typed 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 exact is it, and why four significant figures?

The factors in GROUPS are exact decimals (an inch is 2.54 cm by definition, a pound is 453.59237 g), and a conversion is one multiplication and one division through the group's base unit, so the only rounding is the last step, to four significant figures, with Intl.NumberFormat. That is why 1 cup reads 236.6 ml (the exact value is 236.5882365) and never 236.58823650000001. The dialog says "Rounded to 4 significant figures" under every result. Cups, spoons and fluid ounces are the US customary ones. If you need more digits, change maximumSignificantDigits in SHOWN.

How is temperature different?

Temperatures are not a factor but a formula, so the Temperature tab uses the affine ones: °F = °C × 9 ÷ 5 + 32 and °C = (°F − 32) × 5 ÷ 9, and the line under the rows shows the one in use. A temperature below absolute zero (−273.15 °C, −459.67 °F) is refused in words, "Below absolute zero", and the other field stays empty. Numbers that are not numbers, or that are too large to show, are refused the same way.

Is it accessible from the keyboard?

Yes. The tabs are a real tab list, each number is a number field named From or To, each unit is a native select named From unit or To unit, and Swap and the presets are buttons. The real dialog is named by its title and described by its sentence, focus starts in the From number and stays inside until the dialog closes, and Escape, Cancel and the close button return focus to Convert units. The trigger is the button wrapped in DialogTrigger with asChild, and the dialog is controlled with open and onOpenChange. The other number follows as you type. The copy shown on load moves no focus; Escape inside it, Cancel and its close button close it and put focus on the button.

What stays local in the demo, and how do I wire it up?

It is pure arithmetic, so all of it. Nothing is sent, and the only thing that leaves the dialog is the number Use writes into the drawn recipe line, with a polite message saying nothing was saved. In your app give apply your own write-back (a recipe field, a form value), and add units by adding entries with their exact factor to a group.

How does it look on a phone?

The copy shown on load docks to the foot of the page as a bottom sheet, with the tabs, the two rows and the chips scrolling above the pinned Cancel and Use buttons. The tabs share one height, so switching never moves what is under them, and the chips wrap to a second line when the width needs it. The real dialog keeps the Dialog's own phone layout: a sheet stuck to the bottom of the screen.