export function rentVsBuyCalculator(form: FormTs) {
form.addRow(row => {
row.addTextPanel('header', {
computedValue: () => 'Rent vs Buy Calculator',
customStyles: { 'font-size': '1.5rem', 'font-weight': '600', 'color': '#1e293b' }
});
});
form.addSpacer({ height: 20 });
// Property Details Section
const propertySection = form.addSubform('property', { title: '๐ Property Details' });
propertySection.addRow(row => {
row.addDecimal('homePrice', {
label: 'Home Purchase Price',
min: 50000,
max: 10000000,
defaultValue: 350000,
prefix: '$',
isRequired: true
}, '1fr');
row.addDecimal('downPaymentPercent', {
label: 'Down Payment',
min: 0,
max: 100,
defaultValue: 20,
suffix: '%',
tooltip: 'Typical: 3-20%'
}, '1fr');
});
propertySection.addRow(row => {
row.addDecimal('monthlyRent', {
label: 'Current/Equivalent Monthly Rent',
min: 100,
max: 50000,
defaultValue: 1800,
prefix: '$',
isRequired: true
}, '1fr');
row.addDecimal('rentIncrease', {
label: 'Annual Rent Increase',
min: 0,
max: 15,
defaultValue: 3,
suffix: '%',
tooltip: 'Historical average: 2-4%'
}, '1fr');
});
// Mortgage Details Section
const mortgageSection = form.addSubform('mortgage', { title: '๐ฆ Mortgage Details' });
mortgageSection.addRow(row => {
row.addDecimal('interestRate', {
label: 'Mortgage Interest Rate',
min: 0,
max: 15,
defaultValue: 6.5,
suffix: '%',
isRequired: true
}, '1fr');
row.addDropdown('loanTerm', {
label: 'Loan Term',
options: [
{ id: '15', name: '15 Years' },
{ id: '20', name: '20 Years' },
{ id: '30', name: '30 Years' }
],
defaultValue: '30'
}, '1fr');
});
mortgageSection.addRow(row => {
row.addDecimal('pmiRate', {
label: 'PMI Rate (if down payment < 20%)',
min: 0,
max: 2,
defaultValue: 0.5,
suffix: '%',
tooltip: 'Private Mortgage Insurance, typically 0.3-1.5%',
isVisible: () => (propertySection.decimal('downPaymentPercent')?.value() || 20) < 20
});
});
// Costs Section
const costsSection = form.addSubform('costs', { title: '๐ธ Homeownership Costs' });
costsSection.addRow(row => {
row.addDecimal('propertyTaxRate', {
label: 'Property Tax Rate',
min: 0,
max: 5,
defaultValue: 1.2,
suffix: '%',
tooltip: 'Of home value, annually'
}, '1fr');
row.addDecimal('homeInsurance', {
label: 'Home Insurance (Annual)',
min: 0,
max: 20000,
defaultValue: 1500,
prefix: '$'
}, '1fr');
});
costsSection.addRow(row => {
row.addDecimal('maintenance', {
label: 'Annual Maintenance',
min: 0,
max: 3,
defaultValue: 1,
suffix: '% of value',
tooltip: 'Rule of thumb: 1-2% of home value'
}, '1fr');
row.addDecimal('hoaFees', {
label: 'HOA Fees (Monthly)',
min: 0,
max: 2000,
defaultValue: 0,
prefix: '$'
}, '1fr');
});
costsSection.addRow(row => {
row.addDecimal('closingCosts', {
label: 'Closing Costs',
min: 0,
max: 10,
defaultValue: 3,
suffix: '% of price',
tooltip: 'Typically 2-5% of purchase price'
}, '1fr');
row.addDecimal('rentersInsurance', {
label: 'Renter\'s Insurance (Annual)',
min: 0,
max: 1000,
defaultValue: 180,
prefix: '$'
}, '1fr');
});
// Appreciation & Investment Section
const investmentSection = form.addSubform('investment', { title: '๐ Appreciation & Investment' });
investmentSection.addRow(row => {
row.addDecimal('homeAppreciation', {
label: 'Home Appreciation Rate',
min: -5,
max: 15,
defaultValue: 3,
suffix: '%/year',
tooltip: 'Historical average: 3-4%'
}, '1fr');
row.addDecimal('investmentReturn', {
label: 'Investment Return Rate',
min: 0,
max: 15,
defaultValue: 7,
suffix: '%/year',
tooltip: 'If renting, what return could your down payment earn?'
}, '1fr');
});
// Time Horizon Section
const timeSection = form.addSubform('time', { title: '๐
Time Horizon' });
timeSection.addRow(row => {
row.addDropdown('yearsToStay', {
label: 'How Long Will You Stay?',
options: [
{ id: '3', name: '3 Years' },
{ id: '5', name: '5 Years' },
{ id: '7', name: '7 Years' },
{ id: '10', name: '10 Years' },
{ id: '15', name: '15 Years' },
{ id: '20', name: '20 Years' },
{ id: '30', name: '30 Years' }
],
defaultValue: '7',
isRequired: true,
tooltip: 'Longer stays favor buying'
});
});
timeSection.addRow(row => {
row.addTextPanel('timeNote', {
computedValue: () => {
const years = parseInt(timeSection.dropdown('yearsToStay')?.value() || '7');
if (years <= 3) return 'Tip: Short stays often favor renting due to closing costs and selling fees';
if (years <= 5) return 'Tip: 5 years is often the break-even point for buying vs renting';
return 'Tip: Longer stays allow you to build more equity and spread out transaction costs';
},
customStyles: { 'font-size': '0.85rem', 'color': '#059669', 'font-style': 'italic' }
});
});
// Tax Benefits Section
const taxSection = form.addSubform('tax', { title: '๐งพ Tax Considerations' });
taxSection.addRow(row => {
row.addDropdown('taxBracket', {
label: 'Marginal Tax Bracket',
options: [
{ id: '0', name: 'Standard Deduction (No itemizing)' },
{ id: '12', name: '12%' },
{ id: '22', name: '22%' },
{ id: '24', name: '24%' },
{ id: '32', name: '32%' },
{ id: '35', name: '35%' },
{ id: '37', name: '37%' }
],
defaultValue: '22',
tooltip: 'For mortgage interest and property tax deduction benefits'
});
});
form.addSpacer({ height: 20, showLine: true, lineStyle: 'dashed' });
// Monthly Comparison Section
const monthlySection = form.addSubform('monthly', { title: '๐ Monthly Cost Comparison', isCollapsible: true });
monthlySection.addRow(row => {
row.addPriceDisplay('monthlyMortgage', {
label: 'Mortgage Payment (P&I)',
computedValue: () => {
const homePrice = propertySection.decimal('homePrice')?.value() || 350000;
const downPercent = propertySection.decimal('downPaymentPercent')?.value() || 20;
const rate = (mortgageSection.decimal('interestRate')?.value() || 6.5) / 100 / 12;
const term = parseInt(mortgageSection.dropdown('loanTerm')?.value() || '30') * 12;
const loanAmount = homePrice * (1 - downPercent / 100);
const payment = loanAmount * (rate * Math.pow(1 + rate, term)) / (Math.pow(1 + rate, term) - 1);
return Math.round(payment);
},
variant: 'default'
}, '1fr');
row.addPriceDisplay('totalBuyMonthly', {
label: 'Total Monthly (Buying)',
computedValue: () => {
const homePrice = propertySection.decimal('homePrice')?.value() || 350000;
const downPercent = propertySection.decimal('downPaymentPercent')?.value() || 20;
const rate = (mortgageSection.decimal('interestRate')?.value() || 6.5) / 100 / 12;
const term = parseInt(mortgageSection.dropdown('loanTerm')?.value() || '30') * 12;
const pmiRate = mortgageSection.decimal('pmiRate')?.value() || 0.5;
const loanAmount = homePrice * (1 - downPercent / 100);
const payment = loanAmount * (rate * Math.pow(1 + rate, term)) / (Math.pow(1 + rate, term) - 1);
const propertyTax = (homePrice * (costsSection.decimal('propertyTaxRate')?.value() || 1.2) / 100) / 12;
const insurance = (costsSection.decimal('homeInsurance')?.value() || 1500) / 12;
const maintenance = (homePrice * (costsSection.decimal('maintenance')?.value() || 1) / 100) / 12;
const hoa = costsSection.decimal('hoaFees')?.value() || 0;
let pmi = 0;
if (downPercent < 20) {
pmi = (loanAmount * pmiRate / 100) / 12;
}
return Math.round(payment + propertyTax + insurance + maintenance + hoa + pmi);
},
variant: 'default'
}, '1fr');
});
monthlySection.addRow(row => {
row.addPriceDisplay('monthlyRentDisplay', {
label: 'Monthly Rent (Year 1)',
computedValue: () => {
return propertySection.decimal('monthlyRent')?.value() || 1800;
},
variant: 'default'
}, '1fr');
row.addPriceDisplay('totalRentMonthly', {
label: 'Total Monthly (Renting)',
computedValue: () => {
const rent = propertySection.decimal('monthlyRent')?.value() || 1800;
const rentersIns = (costsSection.decimal('rentersInsurance')?.value() || 180) / 12;
return Math.round(rent + rentersIns);
},
variant: 'default'
}, '1fr');
});
// Results Section
const resultsSection = form.addSubform('results', { title: '๐ฏ Analysis Results', isCollapsible: false });
resultsSection.addRow(row => {
row.addPriceDisplay('totalBuyCost', {
label: 'Total Cost of Buying',
computedValue: () => {
const homePrice = propertySection.decimal('homePrice')?.value() || 350000;
const downPercent = propertySection.decimal('downPaymentPercent')?.value() || 20;
const rate = (mortgageSection.decimal('interestRate')?.value() || 6.5) / 100 / 12;
const termYears = parseInt(mortgageSection.dropdown('loanTerm')?.value() || '30');
const term = termYears * 12;
const years = parseInt(timeSection.dropdown('yearsToStay')?.value() || '7');
const loanAmount = homePrice * (1 - downPercent / 100);
const payment = loanAmount * (rate * Math.pow(1 + rate, term)) / (Math.pow(1 + rate, term) - 1);
const downPayment = homePrice * downPercent / 100;
const closingCosts = homePrice * (costsSection.decimal('closingCosts')?.value() || 3) / 100;
const sellingCosts = homePrice * 0.06;
let totalMortgage = 0;
let totalPropertyTax = 0;
let totalInsurance = 0;
let totalMaintenance = 0;
let totalHoa = 0;
const appreciation = investmentSection.decimal('homeAppreciation')?.value() || 3;
let currentHomeValue = homePrice;
for (let i = 0; i < years; i++) {
totalMortgage += payment * 12;
totalPropertyTax += currentHomeValue * (costsSection.decimal('propertyTaxRate')?.value() || 1.2) / 100;
totalInsurance += costsSection.decimal('homeInsurance')?.value() || 1500;
totalMaintenance += currentHomeValue * (costsSection.decimal('maintenance')?.value() || 1) / 100;
totalHoa += (costsSection.decimal('hoaFees')?.value() || 0) * 12;
currentHomeValue *= (1 + appreciation / 100);
}
const futureHomeValue = homePrice * Math.pow(1 + appreciation / 100, years);
const equity = futureHomeValue - loanAmount;
const totalCost = downPayment + closingCosts + totalMortgage + totalPropertyTax +
totalInsurance + totalMaintenance + totalHoa + sellingCosts - equity;
return Math.round(totalCost);
},
variant: 'default'
}, '1fr');
row.addPriceDisplay('totalRentCost', {
label: 'Total Cost of Renting',
computedValue: () => {
const monthlyRent = propertySection.decimal('monthlyRent')?.value() || 1800;
const rentIncrease = propertySection.decimal('rentIncrease')?.value() || 3;
const years = parseInt(timeSection.dropdown('yearsToStay')?.value() || '7');
const rentersIns = costsSection.decimal('rentersInsurance')?.value() || 180;
const homePrice = propertySection.decimal('homePrice')?.value() || 350000;
const downPercent = propertySection.decimal('downPaymentPercent')?.value() || 20;
const investReturn = investmentSection.decimal('investmentReturn')?.value() || 7;
let totalRent = 0;
let currentRent = monthlyRent;
for (let i = 0; i < years; i++) {
totalRent += currentRent * 12 + rentersIns;
currentRent *= (1 + rentIncrease / 100);
}
const downPayment = homePrice * downPercent / 100;
const investmentGrowth = downPayment * (Math.pow(1 + investReturn / 100, years) - 1);
return Math.round(totalRent - investmentGrowth);
},
variant: 'default'
}, '1fr');
});
// Summary Section
const summarySection = form.addSubform('summary', {
title: '๐ฐ Recommendation',
isCollapsible: false,
sticky: 'bottom'
});
summarySection.addRow(row => {
row.addTextPanel('recommendation', {
computedValue: () => {
const homePrice = propertySection.decimal('homePrice')?.value() || 350000;
const downPercent = propertySection.decimal('downPaymentPercent')?.value() || 20;
const rate = (mortgageSection.decimal('interestRate')?.value() || 6.5) / 100 / 12;
const termYears = parseInt(mortgageSection.dropdown('loanTerm')?.value() || '30');
const term = termYears * 12;
const years = parseInt(timeSection.dropdown('yearsToStay')?.value() || '7');
const monthlyRent = propertySection.decimal('monthlyRent')?.value() || 1800;
const rentIncrease = propertySection.decimal('rentIncrease')?.value() || 3;
const appreciation = investmentSection.decimal('homeAppreciation')?.value() || 3;
const investReturn = investmentSection.decimal('investmentReturn')?.value() || 7;
const loanAmount = homePrice * (1 - downPercent / 100);
const payment = loanAmount * (rate * Math.pow(1 + rate, term)) / (Math.pow(1 + rate, term) - 1);
const downPayment = homePrice * downPercent / 100;
const closingCosts = homePrice * 0.03;
const sellingCosts = homePrice * 0.06;
let totalBuyCost = downPayment + closingCosts;
let currentHomeValue = homePrice;
for (let i = 0; i < years; i++) {
totalBuyCost += payment * 12;
totalBuyCost += currentHomeValue * 0.012;
totalBuyCost += 1500;
totalBuyCost += currentHomeValue * 0.01;
currentHomeValue *= (1 + appreciation / 100);
}
const futureHomeValue = homePrice * Math.pow(1 + appreciation / 100, years);
const equity = futureHomeValue - loanAmount;
totalBuyCost += sellingCosts - equity;
let totalRentCost = 0;
let currentRent = monthlyRent;
for (let i = 0; i < years; i++) {
totalRentCost += currentRent * 12 + 180;
currentRent *= (1 + rentIncrease / 100);
}
const investmentGrowth = downPayment * (Math.pow(1 + investReturn / 100, years) - 1);
totalRentCost -= investmentGrowth;
const difference = Math.abs(totalBuyCost - totalRentCost);
const formatted = difference.toLocaleString('en-US', { style: 'currency', currency: 'USD', maximumFractionDigits: 0 });
if (totalBuyCost < totalRentCost) {
return `๐ BUYING is better by ${formatted} over ${years} years`;
} else if (totalRentCost < totalBuyCost) {
return `๐ข RENTING is better by ${formatted} over ${years} years`;
} else {
return 'โ๏ธ BUYING and RENTING are roughly equal';
}
},
customStyles: { 'font-size': '1.2rem', 'font-weight': '600', 'text-align': 'center', 'color': '#1e293b' }
});
});
summarySection.addRow(row => {
row.addTextPanel('savingsDisplay', {
computedValue: () => {
const years = parseInt(timeSection.dropdown('yearsToStay')?.value() || '7');
const homePrice = propertySection.decimal('homePrice')?.value() || 350000;
const downPercent = propertySection.decimal('downPaymentPercent')?.value() || 20;
const appreciation = investmentSection.decimal('homeAppreciation')?.value() || 3;
const downPayment = homePrice * downPercent / 100;
const futureValue = homePrice * Math.pow(1 + appreciation / 100, years);
const futureEquity = futureValue * 0.4;
return `Potential equity after ${years} years: $${Math.round(futureEquity).toLocaleString()}`;
},
customStyles: { 'font-size': '0.95rem', 'text-align': 'center', 'color': '#059669' }
});
});
summarySection.addRow(row => {
row.addTextPanel('breakeven', {
computedValue: () => {
const homePrice = propertySection.decimal('homePrice')?.value() || 350000;
const closingCosts = homePrice * 0.03;
const sellingCosts = homePrice * 0.06;
const monthlyRent = propertySection.decimal('monthlyRent')?.value() || 1800;
const rate = (mortgageSection.decimal('interestRate')?.value() || 6.5) / 100 / 12;
const downPercent = propertySection.decimal('downPaymentPercent')?.value() || 20;
const term = parseInt(mortgageSection.dropdown('loanTerm')?.value() || '30') * 12;
const loanAmount = homePrice * (1 - downPercent / 100);
const payment = loanAmount * (rate * Math.pow(1 + rate, term)) / (Math.pow(1 + rate, term) - 1);
const monthlyOwning = payment + homePrice * 0.012 / 12 + 125 + homePrice * 0.01 / 12;
const transactionCosts = closingCosts + sellingCosts;
const monthlySavings = monthlyRent - monthlyOwning;
if (monthlySavings > 0) {
const breakeven = Math.ceil(transactionCosts / monthlySavings);
return `Break-even point: ~${Math.round(breakeven / 12)} years`;
}
return 'Monthly ownership costs exceed rent - consider renting';
},
customStyles: { 'font-size': '0.9rem', 'text-align': 'center', 'color': '#475569' }
});
});
summarySection.addRow(row => {
row.addTextPanel('disclaimer', {
computedValue: () => 'This calculator provides estimates only. Actual costs and returns will vary. Consult a financial advisor for personalized advice.',
customStyles: { 'font-size': '0.8rem', 'color': '#94a3b8', 'text-align': 'center' }
});
});
form.configureSubmitButton({
label: 'Get Detailed Report'
});
}