Build an Income Tax Calculator ๐ธ
Income tax is rarely a flat rate. It uses Progressive Brackets (buckets). You fill the first bucket (low rate), then spill over into the next (higher rate). If you are in the "32% bracket," you don't pay 32% on everythingโonly on the money in that bracket.
Step 1: The Data Structure (Brackets) ๐
Define brackets as an array of objects.
Each has a min, max, and rate.
(Using Infinity for the top bracket makes logic easier).
const TAX_BRACKETS = [
{ min: 0, max: 11000, rate: 0.10 },
{ min: 11000, max: 44725, rate: 0.12 },
{ min: 44725, max: 95375, rate: 0.22 },
{ min: 95375, max: 182100, rate: 0.24 },
{ min: 182100, max: Infinity, rate: 0.32 }
];
Step 2: The Algorithm (Bucket Logic) ๐งฎ
Iterate through every bracket. If the salary is greater than the bracket's minimum, calculate the tax for that portion.
The Loop:
let tax = 0;
for (const bracket of TAX_BRACKETS) {
if (salary > bracket.min) {
// Find how much money falls into THIS bracket
const taxableIncome = Math.min(salary, bracket.max) - bracket.min;
// Add tax for this chunk
tax += taxableIncome * bracket.rate;
}
}
Step 3: Effective vs. Marginal Rate ๐
- Marginal Rate: The rate of the highest bracket you touched.
- Effective Rate: Total Tax / Total Income. (The "real" percentage you pay).
Step 4: The React Component ๐ฆ
A straightforward salary input with a breakdown of the calculation.
"use client"
import { useState } from "react"
import { DollarSign } from "lucide-react"
const BRACKETS = [
{ max: 11000, rate: 0.10 },
{ max: 44725, rate: 0.12 },
{ max: 95375, rate: 0.22 },
{ max: Infinity, rate: 0.24 }
]
export default function TaxCalc() {
const [income, setIncome] = useState("")
const calculate = () => {
let salary = Number(income);
let tax = 0;
let prevMax = 0;
for (let b of BRACKETS) {
if (salary > prevMax) {
const taxable = Math.min(salary, b.max) - prevMax;
tax += taxable * b.rate;
prevMax = b.max;
}
}
return tax;
}
const tax = calculate();
const net = Number(income) - tax;
const effectiveRate = income ? ((tax / Number(income)) * 100).toFixed(1) : 0;
return (
<div className="max-w-md mx-auto p-6 bg-emerald-900 rounded-xl text-emerald-50 shadow-2xl">
<div className="mb-6">
<label className="text-xs font-bold text-emerald-400 uppercase">Annual Salary</label>
<div className="relative mt-2">
<DollarSign className="absolute left-3 top-3 text-emerald-500" size={18} />
<input
type="number"
value={income}
onChange={e => setIncome(e.target.value)}
className="w-full pl-10 p-3 bg-emerald-800 border border-emerald-700 rounded-lg outline-none focus:border-emerald-500 text-xl font-bold"
placeholder="50000"
/>
</div>
</div>
<div className="space-y-4">
<div className="p-4 bg-emerald-950 rounded-lg flex justify-between items-center border border-emerald-800">
<span className="text-emerald-400 font-medium">Total Tax</span>
<span className="text-xl font-bold text-red-400">-${tax.toLocaleString()}</span>
</div>
<div className="p-4 bg-emerald-950 rounded-lg flex justify-between items-center border border-emerald-800">
<span className="text-emerald-400 font-medium">Take Home</span>
<span className="text-xl font-bold text-emerald-300">${net.toLocaleString()}</span>
</div>
<div className="text-center text-xs text-emerald-500 mt-4">
Effective Tax Rate: <span className="font-bold text-white">{effectiveRate}%</span>
</div>
</div>
</div>
)
}