BMI calculators are a common beginner project. The math is straightforward: weight in kg divided by height in meters squared. But implementing one that's actually useful reveals some interesting design decisions — particularly around which standard to apply, how to handle different input units, and how to communicate the limitations of the metric clearly.
The Core Calculation
function calculateBMI(weightKg, heightCm) {
const heightM = heightCm / 100;
return weightKg / (heightM * heightM);
}
// Example
const bmi = calculateBMI(70, 175);
// → 22.857...
The Classification Problem
WHO's BMI classifications are widely used:
const WHO_CLASSIFICATIONS = [
{ max: 18.5, label: 'Underweight', risk: 'Malnutrition risk' },
{ max: 25.0, label: 'Normal weight', risk: 'Low risk' },
{ max: 30.0, label: 'Pre-obesity', risk: 'Enhanced risk' },
{ max: 35.0, label: 'Obesity class I', risk: 'Moderate risk' },
{ max: 40.0, label: 'Obesity class II', risk: 'Severe risk' },
{ max: Infinity, label: 'Obesity class III', risk: 'Very severe risk' },
];
But Korean health authorities (대한비만학회) use different thresholds. Asian populations tend to carry proportionally more body fat at the same BMI compared to European populations, and experience obesity-related health risks at lower BMI values:
const KOREAN_CLASSIFICATIONS = [
{ max: 18.5, label: '저체중', labelEn: 'Underweight' },
{ max: 23.0, label: '정상', labelEn: 'Normal' },
{ max: 25.0, label: '비만 전단계', labelEn: 'Pre-obese' },
{ max: 30.0, label: '1단계 비만', labelEn: 'Obesity Class I' },
{ max: 35.0, label: '2단계 비만', labelEn: 'Obesity Class II' },
{ max: Infinity, label: '3단계 비만', labelEn: 'Obesity Class III' },
];
Notice: the Korean standard marks BMI ≥ 23 as "pre-obese" versus WHO's ≥ 25 for "overweight." This 2-point difference can meaningfully change the classification for a large portion of the population.
function classify(bmi, standard = 'korean') {
const classifications = standard === 'korean'
? KOREAN_CLASSIFICATIONS
: WHO_CLASSIFICATIONS;
return classifications.find(c => bmi < c.max);
}
Handling Both Imperial and Metric Input
Users from different regions prefer different units. Supporting both requires careful conversion:
function calculateBMIFromImperial(weightLbs, heightFt, heightIn = 0) {
const totalInches = heightFt * 12 + heightIn;
const heightM = totalInches * 0.0254;
const weightKg = weightLbs * 0.453592;
return weightKg / (heightM * heightM);
}
function calculateBMIFromMetric(weightKg, heightCm) {
const heightM = heightCm / 100;
return weightKg / (heightM * heightM);
}
A React component that handles both:
function BMICalculator() {
const [unit, setUnit] = useState('metric');
const [inputs, setInputs] = useState({
weightKg: '', heightCm: '',
weightLbs: '', heightFt: '', heightIn: '',
});
const [result, setResult] = useState(null);
const calculate = () => {
let bmi;
if (unit === 'metric') {
const w = parseFloat(inputs.weightKg);
const h = parseFloat(inputs.heightCm);
if (!w || !h || h <= 0) return;
bmi = calculateBMIFromMetric(w, h);
} else {
const w = parseFloat(inputs.weightLbs);
const ft = parseFloat(inputs.heightFt) || 0;
const inch = parseFloat(inputs.heightIn) || 0;
if (!w || (!ft && !inch)) return;
bmi = calculateBMIFromImperial(w, ft, inch);
}
setResult({
bmi: Math.round(bmi * 10) / 10,
classification: classify(bmi, 'korean'),
normalRange: calculateNormalRange(
unit === 'metric' ? parseFloat(inputs.heightCm) : null
),
});
};
// ... render
}
Calculating the Normal Weight Range
Users often want to know not just their classification but their target range:
function calculateNormalRange(heightCm, standard = 'korean') {
const heightM = heightCm / 100;
const [minBMI, maxBMI] = standard === 'korean' ? [18.5, 22.9] : [18.5, 24.9];
return {
minKg: Math.round(minBMI * heightM * heightM * 10) / 10,
maxKg: Math.round(maxBMI * heightM * heightM * 10) / 10,
minLbs: Math.round(minBMI * heightM * heightM * 2.20462 * 10) / 10,
maxLbs: Math.round(maxBMI * heightM * heightM * 2.20462 * 10) / 10,
};
}
// Example for 170cm
const range = calculateNormalRange(170, 'korean');
// → { minKg: 53.5, maxKg: 66.2, minLbs: 117.9, maxLbs: 145.9 }
Communicating BMI's Limitations Clearly
BMI is a population-level screening tool, not an individual health assessment. The limitations are real and worth surfacing in your UI:
Muscle vs. fat: BMI measures weight relative to height, not body composition. A highly muscular person can have a high BMI with low body fat. An older person can have a "normal" BMI while having high body fat and low muscle mass.
Distribution: Where fat is stored matters more than total fat. Abdominal fat is metabolically more active and more associated with cardiovascular risk than subcutaneous fat. BMI doesn't capture distribution.
Population-specific: The Korean/Asian standard exists precisely because BMI's predictive value varies by ethnicity. A BMI that predicts a certain level of metabolic risk for a European population predicts different risk levels for an Asian population.
Age and sex: A 60-year-old and a 25-year-old with the same BMI have very different health profiles. Women generally have higher body fat percentages than men at the same BMI.
function BMIDisclaimer() {
return (
<div className="disclaimer">
<p>
BMI is a screening tool, not a diagnostic measure. It doesn't
account for muscle mass, age, sex, or fat distribution.
Consult a healthcare provider for a complete health assessment.
</p>
</div>
);
}
Validation Edge Cases
function validateBMIInputs(weight, height) {
const errors = [];
if (weight <= 0) errors.push('Weight must be positive');
if (height <= 0) errors.push('Height must be positive');
// Sanity checks for human plausible values
if (weight > 500) errors.push('Weight exceeds plausible human range');
if (height < 50 || height > 300) errors.push('Height is outside plausible range (cm)');
// Extremely low BMI values
const bmi = calculateBMIFromMetric(weight, height);
if (bmi < 10) errors.push('Result is below medically possible BMI');
if (bmi > 100) errors.push('Result exceeds medically recorded maximum BMI');
return errors;
}
Try It
ToolZip's BMI calculator applies Korean health standards, supports both metric and imperial input, calculates the normal weight range for your height, and works entirely in the browser.
toolzip.app/tools/bmi-calculator
ToolZip — 48 free browser-based tools. Everything runs client-side.












