Introduction
The BMI calculator is the perfect second Streamlit app: one formula, two inputs, instant feedback — but it teaches a genuinely important pattern, unit systems that reconfigure the UI. Switch between metric and imperial and watch the inputs, labels, and math all change together. If you built the unit converter, this is its health-focused cousin.
Everything fits in about 70 lines, and by the end you’ll be comfortable with conditional UI, radio-driven logic, and rendering a visual scale with a progress bar.
Features
- Metric and imperial — kg/cm or lb/ft+in, switched by a radio.
- Instant result — BMI computed live as inputs change.
- Category feedback — Underweight / Normal / Overweight / Obese with colored status.
- Visual scale — progress bar showing where you sit in the BMI range.
- Healthy weight range — the weight band that would give a normal BMI for your height.
Prerequisites
- Python 3.8+ — from python.org.
- Streamlit — install with pip:
pip install streamlit
Step 1: Create the Script
Save as bmi_app.py:
import streamlit as st
st.set_page_config(page_title="BMI Calculator", page_icon="⚖️")
st.title("⚖️ BMI Calculator")
unit = st.radio("Unit system", ["Metric (kg, cm)", "Imperial (lb, ft/in)"], horizontal=True)
if unit.startswith("Metric"):
weight = st.number_input("Weight (kg)", min_value=1.0, max_value=500.0, value=70.0)
height_cm = st.number_input("Height (cm)", min_value=50.0, max_value=280.0, value=170.0)
height_m = height_cm / 100
else:
weight_lb = st.number_input("Weight (lb)", min_value=2.0, max_value=1100.0, value=154.0)
col1, col2 = st.columns(2)
feet = col1.number_input("Height (ft)", min_value=1, max_value=9, value=5)
inches = col2.number_input("Extra inches", min_value=0, max_value=11, value=7)
weight = weight_lb * 0.453592
height_m = (feet * 12 + inches) * 0.0254
if height_m > 0:
bmi = weight / (height_m ** 2)
if bmi < 18.5:
category, color = "Underweight", "🟡"
elif bmi < 25:
category, color = "Normal weight", "🟢"
elif bmi < 30:
category, color = "Overweight", "🟠"
else:
category, color = "Obese", "🔴"
st.metric("Your BMI", f"{bmi:.1f}")
st.markdown(f"### {color} {category}")
st.progress(min(bmi / 40, 1.0), text=f"BMI scale (0–40): you are at {bmi:.1f}")
low = 18.5 * height_m ** 2
high = 24.9 * height_m ** 2
st.info(f"Healthy weight range for your height: {low:.1f}–{high:.1f} kg")
Step 2: Run the App
streamlit run bmi_app.py
Toggle between unit systems, drag the inputs, and watch the category, color, and scale respond live — no button needed.
How It Works
The whole app is a branching UI. The radio’s value decides which st.number_input widgets render, and because Streamlit reruns the script on every interaction, switching units swaps the form instantly. The math normalizes both systems to kilograms and meters at the top, so the BMI formula and category thresholds are written exactly once — one source of truth instead of duplicated imperial math.
The category ladder is a classic if/elif chain over WHO thresholds (18.5 / 25 / 30). The visual scale is st.progress with the BMI clamped to the 0–40 range — a one-line visualization that reads better than a number alone.
The healthy-range footer inverts the formula: instead of BMI = weight / height², it solves weight = BMI × height² at the two boundary BMIs. Algebra doing UI work.
Common Errors & Fixes
ZeroDivisionError— height defaulted to 0; guard withif height_m > 0:before dividing (already in the code above).- Imperial results look wrong — you mixed conversion constants; pounds→kg is 0.453592, feet+inches→meters is total_inches × 0.0254. Convert once, at the top.
- Radio resets on interaction — give the radio a
key="unit"so its selection survives reruns. - Progress bar overflows —
st.progressexpects 0.0–1.0; clamp withmin(bmi / 40, 1.0).
Key Concepts
- Conditional widgets — different inputs per unit system, swapped by rerun.
- Normalize early — convert units once, keep the core formula single-sourced.
st.metric+st.progress— number plus visual context, zero CSS.- Inverting formulas — derive healthy weight ranges from the same equation.
What to Try Next
- Add age and sex inputs and show BMI percentile context for children (WHO LMS data).
- Store measurements over time in CSV and chart BMI history, like the expense tracker.
- Add a BMR/calories tab using the Mifflin-St Jeor equation with
st.tabs. - Localize labels with a language selectbox — the translator app can even translate them dynamically.
FAQ
Is BMI accurate for athletes?
No — BMI ignores muscle mass, so muscular people often read “overweight.” Treat it as a quick population-level screen, not a diagnosis, and consider waist measurements alongside it.
Why compute in metric internally?
One internal unit system means one tested formula. Converting at the edges (UI in, display out) is the same pivot idea the unit converter uses for temperature.
Can I use sliders instead of number inputs?
Yes — st.slider with float steps feels great for exploration; keep st.number_input for precise entry. Offering both via a checkbox takes three lines.