Build a Universal Unit Converter 📏
You don't need if/else statements for every possible pair (e.g., if meters to feet, if pounds to kg).
Instead, use Base Unit Normalization.
Convert the input to a "Base Unit" (Factor 1.0), then convert to the target.
Step 1: The Data Structure 📊
Define every unit relative to a Base Unit. For Length, let's use Meters as the base.
- Meter: 1.0
- Kilometer: 1000.0 (1000 meters)
- Centimeter: 0.01 (1/100th of a meter)
- Foot: 0.3048
const units = {
length: {
meter: 1,
kilometer: 1000,
centimeter: 0.01,
foot: 0.3048,
inch: 0.0254,
mile: 1609.34
},
weight: {
gram: 1,
kilogram: 1000,
pound: 453.592,
ounce: 28.3495
}
}
Step 2: The Magic Formula ✨
To convert FROM any unit TO any unit:
Result = Input × FromFactor / ToFactor
Example: 100 Feet to Meters
- Input: 100
- From (Foot): 0.3048
- To (Meter): 1
100 * 0.3048 / 1 = 30.48 Meters
Example: 1 Mile to Kilometers
- Input: 1
- From (Mile): 1609.34
- To (Km): 1000
1 * 1609.34 / 1000 = 1.60934 Km
Step 3: The React Component 🧮
We need a generic component that can handle any category.
"use client"
import { useState } from "react"
import { ArrowRightLeft } from "lucide-react"
const FACTORS = {
length: { meter: 1, foot: 0.3048, inch: 0.0254, yard: 0.9144, mile: 1609.34, kilometer: 1000 },
weight: { kilogram: 1, pound: 0.453592, ounce: 0.0283495, gram: 0.001 },
}
export default function Converter() {
const [category, setCategory] = useState('length')
const [fromUnit, setFromUnit] = useState('meter')
const [toUnit, setToUnit] = useState('foot')
const [val, setVal] = useState<number | ''>('')
const result = val !== ''
? (Number(val) * FACTORS[category][fromUnit] / FACTORS[category][toUnit])
: '---';
return (
<div className="max-w-2xl mx-auto p-6 bg-slate-900 rounded-xl border border-slate-700 shadow-2xl">
<div className="flex justify-center mb-8">
<div className="inline-flex bg-slate-800 p-1 rounded-lg">
{Object.keys(FACTORS).map(cat => (
<button
key={cat}
onClick={() => { setCategory(cat); setFromUnit(Object.keys(FACTORS[cat])[0]); setToUnit(Object.keys(FACTORS[cat])[1]); }}
className={`px-4 py-2 rounded-md text-sm font-bold capitalize transition ${category === cat ? 'bg-indigo-500 text-white' : 'text-slate-400 hover:text-white'}`}
>
{cat}
</button>
))}
</div>
</div>
<div className="grid grid-cols-[1fr,auto,1fr] gap-4 items-center">
<div className="space-y-2">
<label className="text-xs font-bold text-slate-500 uppercase">Input</label>
<input
type="number"
value={val}
onChange={e => setVal(Number(e.target.value))}
className="w-full p-3 bg-slate-800 border border-slate-700 rounded-lg text-white"
placeholder="0"
/>
<select
value={fromUnit}
onChange={e => setFromUnit(e.target.value)}
className="w-full p-2 bg-slate-800 border border-slate-700 rounded-lg text-slate-300 capitalize"
>
{Object.keys(FACTORS[category]).map(u => <option key={u} value={u}>{u}</option>)}
</select>
</div>
<div className="pt-6 text-slate-500">
<ArrowRightLeft />
</div>
<div className="space-y-2">
<label className="text-xs font-bold text-slate-500 uppercase">Result</label>
<div className="w-full p-3 bg-slate-950 border border-slate-800 rounded-lg text-indigo-400 font-mono font-bold">
{typeof result === 'number' ? result.toFixed(4) : result}
</div>
<select
value={toUnit}
onChange={e => setToUnit(e.target.value)}
className="w-full p-2 bg-slate-800 border border-slate-700 rounded-lg text-slate-300 capitalize"
>
{Object.keys(FACTORS[category]).map(u => <option key={u} value={u}>{u}</option>)}
</select>
</div>
</div>
</div>
)
}
Step 4: The Exception (Temperature) 🌡️
Temperature cannot use the factor method because it has an offset (plus/minus 32). Zero degrees Celsius is NOT zero degrees Fahrenheit.
You need a separate if block:
if (category === 'temp') {
if (from === 'c' && to === 'f') return (val * 9/5) + 32;
if (from === 'f' && to === 'c') return (val - 32) * 5/9;
}
Always handle temperature as a special case in your conversion function.