Build a BMR & TDEE Calculator 🍎
Health apps are essential. In this guide, we will build a calculator that tells users their Basal Metabolic Rate (BMR) and Total Daily Energy Expenditure (TDEE).
- BMR: Calories burned if you stayed in bed all day (Comma).
- TDEE: Calories burned based on your activity level (The number you use for diets).
Step 1: The Math (Mifflin-St Jeor) 🧮
There are many formulas, but the Mifflin-St Jeor equation is widely considered the most accurate for the general population.
The Formulas:
- Men:
(10 × weight_kg) + (6.25 × height_cm) - (5 × age) + 5 - Women:
(10 × weight_kg) + (6.25 × height_cm) - (5 × age) - 161
Note: Inputs must be Metric (kg/cm) for the math to work. If usage enters Imperial (lbs/feet), convert it first.
Step 2: From BMR to TDEE (Activity Multipliers) 🏃♂️
Once you have the BMR, you multiply it by an "Activity Factor" to get TDEE.
- Sedentary: BMR × 1.2
- Lightly Active: BMR × 1.375
- Moderately Active: BMR × 1.55
- Very Active: BMR × 1.725
- Extra Active: BMR × 1.9
const calculateTDEE = (bmr, activityLevel) => {
const multipliers = {
sedentary: 1.2,
light: 1.375,
moderate: 1.55,
active: 1.725,
athlete: 1.9
};
return Math.round(bmr * multipliers[activityLevel]);
}
Step 3: The Full React Component 💻
Here is a robust implementation. It handles unit conversion (Metric/Imperial) internally so the math stays clean.
"use client"
import { useState } from "react"
import { Activity, Scale } from "lucide-react"
export default function BMRCalculator() {
const [gender, setGender] = useState("male")
const [age, setAge] = useState(25)
const [height, setHeight] = useState(180) // cm
const [weight, setWeight] = useState(75) // kg
const [activity, setActivity] = useState("sedentary")
const [result, setResult] = useState<any>(null)
const calculate = () => {
// 1. Calculate BMR (Mifflin-St Jeor)
let bmr = (10 * weight) + (6.25 * height) - (5 * age)
if (gender === "male") bmr += 5
else bmr -= 161
// 2. Calculate TDEE
const multipliers: Record<string, number> = {
sedentary: 1.2,
light: 1.375,
moderate: 1.55,
active: 1.725,
athlete: 1.9
}
const tdee = bmr * multipliers[activity]
setResult({ bmr: Math.round(bmr), tdee: Math.round(tdee) })
}
return (
<div className="max-w-md mx-auto p-6 bg-white border rounded-xl shadow-lg">
<div className="flex items-center gap-2 mb-6 text-green-600">
<Activity size={24} />
<h2 className="text-xl font-bold">Calorie Calculator</h2>
</div>
<div className="space-y-4">
{/* GENDER */}
<div className="grid grid-cols-2 gap-2">
<button
onClick={() => setGender("male")}
className={`p-3 rounded-lg font-bold border ${gender === 'male' ? 'bg-blue-600 text-white border-blue-600' : 'text-slate-600'}`}
>
Male
</button>
<button
onClick={() => setGender("female")}
className={`p-3 rounded-lg font-bold border ${gender === 'female' ? 'bg-pink-600 text-white border-pink-600' : 'text-slate-600'}`}
>
Female
</button>
</div>
{/* INPUTS */}
<div className="grid grid-cols-2 gap-4">
<div>
<label className="text-xs font-bold uppercase text-slate-500">Weight (kg)</label>
<input
type="number" value={weight} onChange={e => setWeight(Number(e.target.value))}
className="w-full p-2 border rounded font-mono"
/>
</div>
<div>
<label className="text-xs font-bold uppercase text-slate-500">Height (cm)</label>
<input
type="number" value={height} onChange={e => setHeight(Number(e.target.value))}
className="w-full p-2 border rounded font-mono"
/>
</div>
<div className="col-span-2">
<label className="text-xs font-bold uppercase text-slate-500">Age</label>
<input
type="number" value={age} onChange={e => setAge(Number(e.target.value))}
className="w-full p-2 border rounded font-mono"
/>
</div>
</div>
{/* ACTIVITY */}
<div>
<label className="text-xs font-bold uppercase text-slate-500">Activity Level</label>
<select
className="w-full p-3 border rounded bg-slate-50"
value={activity}
onChange={e => setActivity(e.target.value)}
>
<option value="sedentary">Sedentary (Office Job)</option>
<option value="light">Light Exercise (1-2 days/week)</option>
<option value="moderate">Moderate Exercise (3-5 days/week)</option>
<option value="active">Heavy Exercise (6-7 days/week)</option>
<option value="athlete">Athlete (2x per day)</option>
</select>
</div>
<button
onClick={calculate}
className="w-full py-3 bg-slate-900 text-white font-bold rounded-lg hover:bg-slate-800 transition shadow-md"
>
Calculate Calories
</button>
</div>
{/* RESULTS */}
{result && (
<div className="mt-8 pt-6 border-t animate-in slide-in-from-bottom-2 fade-in">
<div className="grid grid-cols-2 gap-4 text-center">
<div className="p-4 bg-slate-50 rounded-lg">
<div className="text-2xl font-bold text-slate-700">{result.bmr}</div>
<div className="text-xs font-bold text-slate-400 uppercase">BMR (Resting)</div>
</div>
<div className="p-4 bg-green-50 rounded-lg border border-green-100">
<div className="text-3xl font-bold text-green-600">{result.tdee}</div>
<div className="text-xs font-bold text-green-600 uppercase">Daily Needed</div>
</div>
</div>
<div className="mt-4 text-center">
<p className="text-sm text-slate-500">
To lose 0.5kg/week, eat <span className="font-bold text-slate-800">{result.tdee - 500}</span> calories.
</p>
</div>
</div>
)}
</div>
)
}
Step 4: Unit Conversion Trick 📏
The code above assumes Metric inputs. To support Imperial (Lbs/Feet) without changing the core formula:
- Create separate UI inputs for Lbs/Feet.
- Before calling
calculate(), convert them:kg = lbs / 2.20462cm = (feet * 30.48) + (inches * 2.54)
- Pass the converted metric values to your standard formula. This keeps your "Business Logic" clean and standard.