I Built a Zero-Backend Unit Converter: 5 Lessons for Devs
A unit converter looks like the easiest tool on the internet. It is a lookup table, right? Multiply by a constant, show the answer, done. Then you build one, and it fights back. I built this one — the ToolkitLoop Unit C
A unit converter looks like the easiest tool on the internet. It is a lookup table, right? Multiply by a constant, show the answer, done.
Then you build one, and it fights back. I built this one — the ToolkitLoop Unit Converter (https://toolkitloop.com/unit-converter) — as a zero-backend, client-side tool: no ads, no signup, results update as you type, and fixed-unit conversions stay in the browser. Here are the five lessons that surprised me most.
1. The input is a parser, not a text field
The tool page invites you to type "10 km to miles", "5′8″ to cm", or "7 lb 4 oz to kg". That single input box is doing real parsing work.
A height like 5′8″ is two numbers in two units. A weight like 7 lb 4 oz is the same. A naive implementation that grabs the first number and the first unit it sees will silently convert only the feet, or only the pounds, and return an answer that looks confident and is wrong.
The shape of the problem is a tiny grammar:
// Illustrative: the shape of a compound-input parser
function parseCompound(input) {
// "5'8"" -> [{ value: 5, unit: "ft" }, { value: 8, unit: "in" }]
// "7 lb 4 oz" -> [{ value: 7, unit: "lb" }, { value: 4, unit: "oz" }]
const parts = [...input.matchAll(/(\d+(?:\.\d+)?)\s*([a-z'"]+)/gi)];
return parts.map(([_, value, unit]) => ({ value: Number(value), unit }));
}
The lesson: the moment your input accepts human phrasing, you own a parser, with all the edge cases that implies. What counts as a unit token? Is ' feet or minutes? Does 5'8" mean the same as 5 ft 8 in? Every "yes" you add to that list is a test case you now have to maintain.
2. Temperature is not a ratio — it is an affine transform
Most conversions are a multiplication: value × factor. Temperature breaks that pattern. Celsius to Fahrenheit is multiply by 9/5, then add 32. Fahrenheit to Celsius is subtract 32, then multiply by 5/9. Kelvin shifts everything by 273.15.
// Illustrative: affine vs ratio conversions
function convert(value, from, to) {
// Ratio conversions: { factor }
// Affine conversions: { scale, offset } — apply scale, then offset
const toBase = (value - from.offset) / from.scale;
return toBase * to.scale + to.offset;
}
If your conversion table stores only a factor per unit, temperature will be wrong everywhere except at the single point where the two scales happen to cross. The fix is to store a scale and an offset for every unit and treat ratio conversions as the special case where the offset is zero. That one design decision — always carry both numbers — removes an entire class of bug.
It also explains why a generic "convert anything" function that only multiplies is a trap. The moment someone adds a temperature unit to it, every temperature answer goes wrong, quietly.
3. US vs Imperial is a labelling problem, not a maths problem
The tool labels US and Imperial units separately, and that label is doing more work than the arithmetic. A US gallon and an Imperial gallon are different volumes. Same name, different size.
This is a data-modelling lesson: the unit's identity is not its name, it is its name plus its system. If your internal key is just "gallon", you have already lost the distinction, and no amount of correct arithmetic downstream can recover it. The key has to be something like gallon_us vs gallon_imperial, and the UI has to show the system to the user — otherwise the user cannot tell which one they picked, and a wrong-by-20% answer looks perfectly plausible.
The same pattern shows up everywhere in software: two things with the same display name and different meanings, collapsed into one key. The fix is always the same — make the key carry the distinction, and make the UI show it.
4. Precision is a UX decision, not a formatting one
The converter lets you set precision, and that choice matters more than it looks. Show too few decimals and the answer is useless for the task. Show too many and you are lying: a long decimal implies an exactness the input never had.
A good rule of thumb I landed on: precision should match the task, not the calculation. Travel distances need one or two decimals. Cooking needs whole numbers or one decimal. And if the input was approximate — "about 10 km" — the answer is approximate too, however many digits you print.
// Illustrative: round for display, never for the next step
function display(value, decimals) {
const factor = 10 ** decimals;
return Math.round(value * factor) / factor;
}
// Keep the full-precision value internally; round only at the edge, and let the user choose how many decimals they see.
Rounding is a display concern. The moment you round the stored value, every chained conversion inherits the error. Keep full precision internally, round only at the edge, and let the user choose how many decimals they see.
5. "Instant" is a contract with the input loop
The tool has no convert button: results update as you type or change units. That sounds like a small detail. It changes the architecture.
With a button, conversion runs once, on demand. Without one, it runs on every keystroke, which means it has to be cheap, synchronous, and total — it must return something sensible for every intermediate state, including a half-typed number, an empty field, and a unit pair that does not make sense together.
That constraint pushes you toward a design where conversion is a pure function of (value, from, to): no network, no async, no state beyond the three inputs. Which is exactly why the tool can be zero-backend and keep everything in the browser. The UX requirement (instant) and the architecture (client-side only, no signup, no ads) are the same decision seen from two sides.
What I would argue about
Here is the part I am least sure about, and the question I want to leave open: what is the hardest part of a unit converter — the parsing, the affine maths, or the labelling?
My instinct after building it is the labelling. The maths is a solved problem once you carry scale plus offset. The parsing is bounded — there are only so many ways people write a height. But the labelling problem never ends, because every new unit system, every regional variant, and every same-name-different-size pair is another distinction the UI has to carry without confusing the person who just wants to know how many miles 10 km is.
If you have built a converter, a calculator, or anything that takes human phrasing as input: what broke first for you — the parser, the maths, or the labels? And is there a unit pair you still do not trust your own tool with?
Disclosure: I built this tool. The Unit Converter (https://toolkitloop.com/unit-converter) is part of ToolkitLoop, a set of free browser-side tools — no ads, no signup, and conversions happen in your browser.
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.