<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Bond Repayment Calculator South Africa 2026 — Full Amortisation Schedule | Calcunomics</title>
<meta name="description" content="Calculate your exact monthly bond repayment with a full amortisation schedule. See how extra payments save interest. Uses current SA prime rate of 10.25%. Free, no sign-up.">
<meta name="robots" content="index, follow">
<link rel="canonical" href="https://www.calcunomics.com/calculators/bond-repayment/">
<meta property="og:title" content="Bond Repayment Calculator South Africa 2026 — Full Amortisation Schedule">
<meta property="og:description" content="Monthly bond repayment, total interest, and a full year-by-year amortisation schedule. See exactly how extra payments reduce your term and save money.">
<meta property="og:url" content="https://www.calcunomics.com/calculators/bond-repayment/">
<meta property="og:type" content="website">
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{"@type":"ListItem","position":1,"name":"Home","item":"https://www.calcunomics.com/"},
{"@type":"ListItem","position":2,"name":"Calculators","item":"https://www.calcunomics.com/calculators/"},
{"@type":"ListItem","position":3,"name":"Bond Repayment Calculator","item":"https://www.calcunomics.com/calculators/bond-repayment/"}
]
}
</script>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "WebApplication",
"name": "South Africa Bond Repayment Calculator 2026",
"url": "https://www.calcunomics.com/calculators/bond-repayment/",
"description": "Calculate your monthly bond repayment with a full amortisation schedule and extra payment savings. Uses the current SA prime rate of 10.25%.",
"applicationCategory": "FinanceApplication",
"operatingSystem": "Any",
"offers": {"@type":"Offer","price":"0","priceCurrency":"ZAR"}
}
</script>
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": [
{
"@type": "Question",
"name": "What is the monthly repayment on a R1 million bond in South Africa?",
"acceptedAnswer": {
"@type": "Answer",
"text": "At the current prime rate of 10.25% over 20 years, the monthly repayment on a R1,000,000 bond is approximately R9,915. Over the full 20-year term, total repayments would be R2,379,600 — meaning you pay R1,379,600 in interest on top of the R1 million principal. Adding R1,000 extra per month reduces the term by approximately 3 years and saves around R250,000 in interest."
}
},
{
"@type": "Question",
"name": "How does an amortisation schedule work in South Africa?",
"acceptedAnswer": {
"@type": "Answer",
"text": "An amortisation schedule shows how each monthly bond repayment is split between interest and principal reduction. In the early years, most of your repayment goes to interest — on a 20-year bond at 10.25%, roughly 85% of your first payment is interest and only 15% reduces what you owe. This ratio gradually shifts until the final payments are almost entirely principal. The schedule shows your outstanding balance at every point in the loan."
}
},
{
"@type": "Question",
"name": "Does paying extra into your bond save money in South Africa?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Yes — significantly. Every extra rand paid into a South African bond reduces the outstanding principal immediately, which reduces the interest charged the following month. On a R1.5 million bond at 10.25% over 20 years, paying an extra R2,000 per month saves approximately R420,000 in total interest and cuts the loan term by roughly 4.5 years. The saving is higher the earlier in the loan term you start paying extra."
}
},
{
"@type": "Question",
"name": "What is the difference between a bond repayment calculator and a home loan calculator?",
"acceptedAnswer": {
"@type": "Answer",
"text": "A home loan calculator typically starts from the property price and deposit to determine the loan amount, then calculates the monthly repayment. A bond repayment calculator starts from the loan amount directly and focuses on the repayment schedule — including the full amortisation table showing how the balance reduces month by month, and the impact of extra payments. Both are useful at different stages of the property purchase process."
}
},
{
"@type": "Question",
"name": "How does the South African prime rate affect my bond repayment?",
"acceptedAnswer": {
"@type": "Answer",
"text": "Most South African home loans are variable-rate, meaning your monthly repayment moves with the prime lending rate. The prime rate is set at repo rate plus 3.5% — currently 10.25% (repo 6.75%). When the SARB raises or cuts the repo rate, prime moves by the same amount and your bond repayment adjusts. On a R1 million bond, a 0.5% rate increase adds approximately R330 to the monthly repayment."
}
}
]
}
</script>
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--ink: #0F172A;
--ink-2: #334155;
--ink-3: #64748B;
--ink-4: #94A3B8;
--surface: #FFFFFF;
--bg: #F8FAFC;
--border: #E2E8F0;
--slate: #1E3A5F;
--slate-mid: #2D5186;
--slate-light: #EFF6FF;
--slate-pale: #DBEAFE;
--green: #059669;
--green-light: #D1FAE5;
--green-bg: #ECFDF5;
--red: #DC2626;
--red-light: #FEE2E2;
--amber: #D97706;
--amber-light: #FEF3C7;
--orange: #EA580C;
--radius: 10px;
--radius-lg: 16px;
--shadow-md: 0 4px 20px rgba(0,0,0,0.08);
}
html { scroll-behavior: smooth; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif;
font-size: 16px; line-height: 1.7; color: var(--ink); background: var(--bg);
}
.page-wrap { max-width: 820px; margin: 0 auto; padding: 0 20px 64px; }
.breadcrumb { font-size: 13px; color: var(--ink-4); padding: 20px 0 0; margin-bottom: 24px; }
.breadcrumb a { color: var(--ink-3); text-decoration: none; }
.breadcrumb a:hover { color: var(--slate); }
.breadcrumb span { margin: 0 6px; }
.rate-tag {
display: inline-flex; align-items: center; gap: 6px;
font-size: 12px; font-weight: 700;
background: var(--slate-light); color: var(--slate);
border: 1px solid var(--slate-pale);
padding: 4px 12px; border-radius: 20px; margin-bottom: 14px;
}
.rate-tag::before { content: '●'; font-size: 8px; color: var(--slate-mid); }
h1 {
font-size: clamp(24px, 4vw, 34px); font-weight: 800;
color: var(--ink); line-height: 1.15;
letter-spacing: -0.025em; margin-bottom: 14px;
}
.lead {
font-size: 16px; color: var(--ink-2); line-height: 1.75;
border-left: 3px solid var(--slate-mid); padding-left: 16px;
}
/* Calc card */
.calc-card {
background: var(--surface); border: 1px solid var(--border);
border-radius: var(--radius-lg); box-shadow: var(--shadow-md);
overflow: hidden; margin: 28px 0;
}
.calc-header {
background: var(--slate); padding: 20px 24px;
display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 12px;
}
.calc-title { font-size: 15px; font-weight: 700; color: #fff; }
.calc-sub { font-size: 12px; color: rgba(255,255,255,0.6); margin-top: 2px; }
.rate-chip {
background: rgba(255,255,255,0.12); color: rgba(255,255,255,0.9);
font-size: 12px; font-weight: 700; padding: 5px 14px; border-radius: 20px;
white-space: nowrap;
}
.calc-body { padding: 24px; }
/* Inputs */
.input-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-bottom: 16px; }
.input-grid.three { grid-template-columns: 1fr 1fr 1fr; }
@media(max-width:560px) { .input-grid, .input-grid.three { grid-template-columns: 1fr; } }
.field { display: flex; flex-direction: column; gap: 6px; }
label { font-size: 11px; font-weight: 700; color: var(--ink-3); text-transform: uppercase; letter-spacing: 0.06em; }
.field-hint { font-size: 11px; color: var(--ink-4); margin-top: 2px; }
.input-wrap { position: relative; }
.pfx, .sfx {
position: absolute; top: 50%; transform: translateY(-50%);
font-size: 13px; font-weight: 500; color: var(--ink-4); pointer-events: none;
}
.pfx { left: 11px; } .sfx { right: 11px; }
.field input {
width: 100%; font-size: 15px; font-weight: 600; color: var(--ink);
background: var(--bg); border: 1.5px solid var(--border);
border-radius: var(--radius); padding: 11px 36px;
outline: none; transition: border-color 0.15s; -moz-appearance: textfield;
}
.field input.nopad-l { padding-left: 12px; }
.field input:focus { border-color: var(--slate-mid); }
input::-webkit-outer-spin-button, input::-webkit-inner-spin-button { -webkit-appearance: none; }
/* Extra payment toggle */
.extra-toggle {
display: flex; align-items: center; gap: 10px;
font-size: 14px; font-weight: 500; color: var(--ink-2);
margin-bottom: 16px; cursor: pointer; user-select: none;
}
.toggle-track {
width: 40px; height: 22px; border-radius: 11px;
background: var(--border); position: relative; transition: background 0.2s; flex-shrink: 0;
}
.toggle-track.on { background: var(--green); }
.toggle-thumb {
width: 18px; height: 18px; border-radius: 50%; background: #fff;
position: absolute; top: 2px; left: 2px;
transition: left 0.2s; box-shadow: 0 1px 3px rgba(0,0,0,0.2);
}
.toggle-track.on .toggle-thumb { left: 20px; }
.extra-field { display: none; margin-bottom: 16px; }
.extra-field.visible { display: block; }
.calc-btn {
width: 100%; padding: 14px; background: var(--slate); color: #fff;
font-size: 15px; font-weight: 700; border: none; border-radius: var(--radius);
cursor: pointer; letter-spacing: 0.01em; transition: background 0.15s, transform 0.1s;
}
.calc-btn:hover { background: var(--slate-mid); }
.calc-btn:active { transform: scale(0.99); }
/* Results */
.results { display: none; margin-top: 24px; border-top: 1px solid var(--border); padding-top: 24px; }
.results.visible { display: block; }
/* Hero stats */
.hero-grid { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 12px; margin-bottom: 20px; }
@media(max-width:500px) { .hero-grid { grid-template-columns: 1fr 1fr; } }
.hero-card { border-radius: var(--radius); padding: 18px 16px; text-align: center; }
.hero-card.monthly { background: var(--slate); }
.hero-card.total { background: var(--red-light); border: 1px solid #FECACA; }
.hero-card.interest { background: var(--amber-light); border: 1px solid #FDE68A; }
.hero-label { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 6px; }
.hero-card.monthly .hero-label { color: rgba(255,255,255,0.7); }
.hero-card.total .hero-label { color: var(--red); }
.hero-card.interest .hero-label { color: var(--amber); }
.hero-value { font-size: clamp(20px,3vw,28px); font-weight: 800; line-height: 1; letter-spacing: -0.02em; }
.hero-card.monthly .hero-value { color: #fff; }
.hero-card.total .hero-value { color: var(--red); }
.hero-card.interest .hero-value { color: var(--amber); }
.hero-sub { font-size: 11px; margin-top: 5px; }
.hero-card.monthly .hero-sub { color: rgba(255,255,255,0.55); }
.hero-card.total .hero-sub { color: #F87171; }
.hero-card.interest .hero-sub { color: #FCD34D; }
/* Extra payment savings banner */
.savings-banner {
background: var(--green-bg); border: 1px solid #A7F3D0;
border-radius: var(--radius); padding: 14px 18px;
display: none; margin-bottom: 20px;
}
.savings-banner.visible { display: flex; gap: 16px; flex-wrap: wrap; align-items: center; }
.savings-label { font-size: 13px; font-weight: 700; color: var(--green); text-transform: uppercase; letter-spacing: 0.05em; }
.savings-grid { display: flex; gap: 20px; flex-wrap: wrap; }
.saving-item { text-align: center; }
.saving-val { font-size: 20px; font-weight: 800; color: var(--green); }
.saving-desc { font-size: 11px; color: #065F46; }
/* Stat row */
.stat-row { display: grid; grid-template-columns: repeat(4,1fr); gap: 10px; margin-bottom: 24px; }
@media(max-width:500px) { .stat-row { grid-template-columns: 1fr 1fr; } }
.stat-cell { background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius); padding: 11px 12px; }
.stat-lbl { font-size: 11px; font-weight: 600; color: var(--ink-4); text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 3px; }
.stat-val { font-size: 14px; font-weight: 700; color: var(--ink); }
/* Balance bar */
.balance-bar-wrap { margin-bottom: 24px; }
.bar-label { display: flex; justify-content: space-between; font-size: 12px; color: var(--ink-3); margin-bottom: 6px; }
.bar-track { height: 10px; background: var(--border); border-radius: 5px; overflow: hidden; display: flex; }
.bar-seg { height: 100%; transition: width 0.5s ease; }
.seg-principal { background: var(--slate); }
.seg-interest { background: var(--orange); }
.bar-legend { display: flex; gap: 14px; margin-top: 7px; flex-wrap: wrap; }
.legend-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
.legend-item { display: flex; align-items: center; gap: 5px; font-size: 11px; color: var(--ink-3); }
/* Rate sensitivity */
.sensitivity-title { font-size: 13px; font-weight: 700; color: var(--ink-2); text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 10px; }
.rate-table-wrap { border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; margin-bottom: 24px; }
.rate-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.rate-table th {
background: var(--bg); padding: 8px 12px; text-align: left;
font-size: 11px; font-weight: 700; color: var(--ink-4);
text-transform: uppercase; letter-spacing: 0.05em; border-bottom: 1px solid var(--border);
}
.rate-table td { padding: 9px 12px; border-bottom: 1px solid var(--border); color: var(--ink-2); }
.rate-table tr:last-child td { border-bottom: none; }
.rate-table tr.current-rate td { background: var(--slate-light); font-weight: 600; color: var(--slate); }
.rate-table td.up { color: var(--red); font-weight: 600; }
.rate-table td.down { color: var(--green); font-weight: 600; }
/* Amortisation table */
.amort-header {
display: flex; justify-content: space-between; align-items: center;
margin-bottom: 12px; flex-wrap: wrap; gap: 8px;
}
.amort-title { font-size: 13px; font-weight: 700; color: var(--ink-2); text-transform: uppercase; letter-spacing: 0.05em; }
.view-toggle { display: flex; gap: 0; border: 1px solid var(--border); border-radius: 8px; overflow: hidden; }
.vt-btn {
padding: 5px 14px; border: none; background: transparent;
font-size: 12px; font-weight: 600; color: var(--ink-3); cursor: pointer; transition: all 0.15s;
}
.vt-btn.active { background: var(--slate); color: #fff; }
.amort-table-wrap { border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; max-height: 420px; overflow-y: auto; }
.amort-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.amort-table thead th {
position: sticky; top: 0; z-index: 1;
background: var(--slate); color: #fff;
padding: 9px 12px; text-align: right; font-size: 11px; font-weight: 700;
text-transform: uppercase; letter-spacing: 0.04em;
}
.amort-table thead th:first-child { text-align: left; }
.amort-table td { padding: 8px 12px; text-align: right; border-bottom: 1px solid var(--border); color: var(--ink-2); }
.amort-table td:first-child { text-align: left; font-weight: 600; color: var(--ink); }
.amort-table tr:last-child td { border-bottom: none; }
.amort-table tr:nth-child(even) td { background: #FAFAFA; }
.amort-table td.interest-col { color: var(--orange); }
.amort-table td.balance-col { font-weight: 600; }
.amort-table tr.year-row td { background: var(--slate-light); font-weight: 700; color: var(--slate); }
.amort-table td.principal-col { color: var(--green); }
/* Content */
.section { margin: 44px 0; }
h2 { font-size: 22px; font-weight: 800; color: var(--ink); margin-bottom: 14px; letter-spacing: -0.02em; }
h3 { font-size: 17px; font-weight: 700; color: var(--ink); margin-bottom: 10px; }
p { color: var(--ink-2); margin-bottom: 14px; line-height: 1.75; }
p:last-child { margin-bottom: 0; }
.cta-banner {
background: var(--green-bg); border: 1px solid #A7F3D0;
border-radius: var(--radius); padding: 14px 18px;
font-size: 14px; color: #065F46; margin: 28px 0;
display: flex; gap: 10px; align-items: flex-start;
}
.cta-banner a { color: #047857; font-weight: 700; text-decoration: none; }
/* Info grid */
.info-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
@media(max-width:500px) { .info-grid { grid-template-columns: 1fr; } }
.info-card {
background: var(--surface); border: 1px solid var(--border);
border-radius: var(--radius); padding: 16px;
}
.info-card-title { font-size: 13px; font-weight: 700; color: var(--ink); margin-bottom: 6px; }
.info-card-body { font-size: 13px; color: var(--ink-3); line-height: 1.6; }
.info-card-body strong { color: var(--ink-2); font-weight: 600; }
/* FAQ */
.faq { border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
.faq-item { border-bottom: 1px solid var(--border); }
.faq-item:last-child { border-bottom: none; }
.faq-q {
width: 100%; text-align: left; padding: 15px 18px;
font-size: 15px; font-weight: 600; color: var(--ink);
background: var(--surface); border: none; cursor: pointer;
display: flex; justify-content: space-between; align-items: center; gap: 12px;
transition: background 0.1s;
}
.faq-q:hover { background: var(--bg); }
.faq-icon { font-size: 18px; color: var(--ink-4); flex-shrink: 0; transition: transform 0.2s; }
.faq-a { display: none; padding: 0 18px 15px; font-size: 14px; line-height: 1.7; color: var(--ink-2); background: var(--surface); }
.faq-a a { color: var(--slate-mid); }
.faq-item.open .faq-a { display: block; }
.faq-item.open .faq-icon { transform: rotate(45deg); }
/* Related */
.cards-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(168px,1fr)); gap: 12px; }
.rel-card {
background: var(--surface); border: 1px solid var(--border);
border-radius: var(--radius); padding: 16px; text-decoration: none;
display: flex; flex-direction: column; gap: 6px; transition: border-color 0.15s;
}
.rel-card:hover { border-color: var(--slate-mid); }
.rel-card-icon { font-size: 22px; margin-bottom: 2px; }
.rel-card-title { font-size: 14px; font-weight: 700; color: var(--ink); }
.rel-card-desc { font-size: 12px; color: var(--ink-4); line-height: 1.4; }
.guide-links { display: flex; flex-direction: column; gap: 8px; }
.guide-link {
display: flex; align-items: center; gap: 10px; padding: 12px 14px;
background: var(--surface); border: 1px solid var(--border);
border-radius: var(--radius); text-decoration: none;
font-size: 14px; font-weight: 600; color: var(--ink); transition: border-color 0.15s;
}
.guide-link:hover { border-color: var(--slate-mid); }
.guide-link::after { content: '→'; margin-left: auto; color: var(--ink-4); font-weight: 400; }
.disclaimer {
background: var(--amber-light); border: 1px solid #FDE68A;
border-radius: var(--radius); padding: 14px 16px;
font-size: 12px; color: #92400E; line-height: 1.6; margin-top: 40px;
}
</style>
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-4602116331624874"
crossorigin="anonymous"></script>
</head>
<body>
<div class="page-wrap">
<nav class="breadcrumb" aria-label="Breadcrumb">
<a href="homepage.html">Home</a><span>›</span>
<a href="calculators-hub.html">Calculators</a><span>›</span>
Bond Repayment Calculator
</nav>
<header style="margin-bottom:0">
<div class="rate-tag">Prime rate: 10.25% — updated May 2026</div>
<h1>Bond Repayment Calculator South Africa 2026</h1>
<p class="lead">Enter your loan amount, interest rate, and term to get your monthly repayment, full amortisation schedule, and exactly how much you save by paying extra. Uses the current SA prime rate of 10.25%.</p>
</header>
<!-- Calculator -->
<div class="calc-card" id="calculator">
<div class="calc-header">
<div>
<div class="calc-title">Bond Repayment & Amortisation Calculator</div>
<div class="calc-sub">South Africa · Prime 10.25% (repo 6.75%) · Updated May 2026</div>
</div>
<div class="rate-chip">Full schedule included</div>
</div>
<div class="calc-body">
<div class="input-grid">
<div class="field">
<label for="loan-amount">Loan amount</label>
<div class="input-wrap">
<span class="pfx">R</span>
<input type="number" id="loan-amount" value="1500000" min="10000" step="10000">
</div>
<div class="field-hint">The bond amount — property price minus deposit</div>
</div>
<div class="field">
<label for="interest-rate">Interest rate</label>
<div class="input-wrap">
<input type="number" id="interest-rate" value="10.25" min="1" max="30" step="0.25" class="nopad-l" style="padding-left:12px">
<span class="sfx">% p.a.</span>
</div>
<div class="field-hint">Prime is 10.25%. Your rate may differ</div>
</div>
</div>
<div class="input-grid">
<div class="field">
<label for="loan-term">Loan term</label>
<div class="input-wrap">
<input type="number" id="loan-term" value="20" min="1" max="30" class="nopad-l" style="padding-left:12px">
<span class="sfx">years</span>
</div>
<div class="field-hint">Standard SA bonds run 20 years</div>
</div>
<div class="field">
<label for="start-year">Bond start year</label>
<div class="input-wrap">
<input type="number" id="start-year" value="2026" min="2020" max="2040" class="nopad-l" style="padding-left:12px">
</div>
<div class="field-hint">Used to label the amortisation schedule</div>
</div>
</div>
<!-- Extra payment toggle -->
<label class="extra-toggle" onclick="toggleExtra()">
<div class="toggle-track" id="extra-track"><div class="toggle-thumb"></div></div>
<span>Show extra monthly payment savings</span>
</label>
<div class="extra-field" id="extra-field">
<div class="field" style="max-width:280px">
<label for="extra-payment">Additional monthly payment</label>
<div class="input-wrap">
<span class="pfx">R</span>
<input type="number" id="extra-payment" value="2000" min="0" step="500">
</div>
<div class="field-hint">Paid on top of the minimum repayment</div>
</div>
</div>
<button class="calc-btn" onclick="calculate()">Calculate repayment & schedule</button>
<!-- Results -->
<div class="results" id="results">
<div class="hero-grid">
<div class="hero-card monthly">
<div class="hero-label">Monthly repayment</div>
<div class="hero-value" id="r-monthly">R0</div>
<div class="hero-sub" id="r-monthly-sub">over 20 years at 10.25%</div>
</div>
<div class="hero-card total">
<div class="hero-label">Total repaid</div>
<div class="hero-value" id="r-total">R0</div>
<div class="hero-sub">over full term</div>
</div>
<div class="hero-card interest">
<div class="hero-label">Total interest</div>
<div class="hero-value" id="r-interest">R0</div>
<div class="hero-sub" id="r-interest-pct">0% of total</div>
</div>
</div>
<!-- Extra payment savings -->
<div class="savings-banner" id="savings-banner">
<div>
<div class="savings-label">💰 Extra payment savings</div>
</div>
<div class="savings-grid">
<div class="saving-item">
<div class="saving-val" id="s-interest">R0</div>
<div class="saving-desc">Interest saved</div>
</div>
<div class="saving-item">
<div class="saving-val" id="s-months">0 months</div>
<div class="saving-desc">Term reduced by</div>
</div>
<div class="saving-item">
<div class="saving-val" id="s-payoff">—</div>
<div class="saving-desc">Paid off by</div>
</div>
</div>
</div>
<!-- Stat row -->
<div class="stat-row">
<div class="stat-cell">
<div class="stat-lbl">Loan amount</div>
<div class="stat-val" id="r-principal">R0</div>
</div>
<div class="stat-cell">
<div class="stat-lbl">Interest ratio</div>
<div class="stat-val" id="r-ratio">0%</div>
</div>
<div class="stat-cell">
<div class="stat-lbl">Monthly interest (yr 1)</div>
<div class="stat-val" id="r-first-int">R0</div>
</div>
<div class="stat-cell">
<div class="stat-lbl">Payoff year</div>
<div class="stat-val" id="r-payoff-yr">—</div>
</div>
</div>
<!-- Balance bar -->
<div class="balance-bar-wrap">
<div class="bar-label">
<span>Total cost breakdown</span>
<span id="bar-label-right"></span>
</div>
<div class="bar-track">
<div class="bar-seg seg-principal" id="bar-principal" style="width:50%"></div>
<div class="bar-seg seg-interest" id="bar-interest" style="width:50%"></div>
</div>
<div class="bar-legend">
<div class="legend-item"><div class="legend-dot" style="background:var(--slate)"></div>Principal</div>
<div class="legend-item"><div class="legend-dot" style="background:var(--orange)"></div>Interest</div>
</div>
</div>
<!-- Rate sensitivity -->
<div class="sensitivity-title">How your repayment changes with rate movements</div>
<div class="rate-table-wrap">
<table class="rate-table" id="rate-table">
<thead>
<tr>
<th>Scenario</th>
<th>Rate</th>
<th>Monthly repayment</th>
<th>vs current</th>
<th>Total interest</th>
</tr>
</thead>
<tbody id="rate-tbody"></tbody>
</table>
</div>
<!-- Amortisation schedule -->
<div class="amort-header">
<div class="amort-title">Amortisation schedule</div>
<div class="view-toggle">
<button class="vt-btn active" id="vt-annual" onclick="setView('annual')">Annual</button>
<button class="vt-btn" id="vt-monthly" onclick="setView('monthly')">Monthly</button>
</div>
</div>
<div class="amort-table-wrap">
<table class="amort-table">
<thead>
<tr>
<th>Period</th>
<th>Payment</th>
<th>Principal</th>
<th>Interest</th>
<th>Balance</th>
</tr>
</thead>
<tbody id="amort-tbody"></tbody>
</table>
</div>
<p style="font-size:12px;color:var(--ink-4);margin-top:8px;">Scroll to view full schedule. Interest is calculated monthly on the outstanding balance. Minor rounding differences may appear in the final period.</p>
</div>
</div>
</div>
<!-- How amortisation works -->
<div class="section">
<h2>How South African bond amortisation works</h2>
<p>Every bond repayment is split into two components: interest charged on the outstanding balance, and principal reduction. In the early years of a South African home loan, the interest component dominates — this is why your balance barely moves in the first few years even though you are making full payments every month.</p>
<div class="info-grid">
<div class="info-card">
<div class="info-card-title">Year 1 on a R1.5m bond at 10.25%</div>
<div class="info-card-body">Monthly repayment: <strong>~R14,861</strong><br>Month 1 interest: <strong>~R12,813</strong><br>Month 1 principal: <strong>~R2,048</strong><br>Interest share: <strong>~86%</strong> of payment</div>
</div>
<div class="info-card">
<div class="info-card-title">Year 18 on the same bond</div>
<div class="info-card-body">Monthly repayment: <strong>~R14,861</strong><br>Month interest: <strong>~R3,200</strong><br>Month principal: <strong>~R11,661</strong><br>Interest share: <strong>~22%</strong> of payment</div>
</div>
<div class="info-card">
<div class="info-card-title">The case for extra payments</div>
<div class="info-card-body">Extra payments go entirely to principal. Reducing the balance in year 1 saves interest on that reduced amount for every remaining year — the saving compounds over time.</div>
</div>
<div class="info-card">
<div class="info-card-title">When interest is calculated</div>
<div class="info-card-body">SA banks calculate bond interest <strong>daily</strong> on the outstanding balance and debit it monthly. A mid-month payment reduces the daily interest accrual immediately — not just from the next statement date.</div>
</div>
</div>
</div>
<!-- CTA -->
<div class="cta-banner">
<span>🏠</span>
<div>Not sure what loan amount to enter? Use our <a href="home-loan-calculator.html">home loan affordability calculator</a> to determine how much you qualify for based on your gross salary, existing debt, and deposit — then come back here to run the amortisation schedule.
</div>
</div>
<!-- FAQ -->
<div class="section">
<h2>Frequently asked questions</h2>
<div class="faq">
<div class="faq-item">
<button class="faq-q" onclick="toggleFAQ(this)">
What is the monthly repayment on a R1 million bond?
<span class="faq-icon">+</span>
</button>
<div class="faq-a">At the current prime rate of 10.25% over 20 years, the monthly repayment on a R1,000,000 bond is approximately R9,915. Over the full term, total repayments come to R2,379,600 — meaning you pay R1,379,600 in interest on top of the R1 million principal. Adding R1,000 extra per month reduces the term by approximately 3 years and saves around R250,000 in interest.</div>
</div>
<div class="faq-item">
<button class="faq-q" onclick="toggleFAQ(this)">
How does an amortisation schedule work in South Africa?
<span class="faq-icon">+</span>
</button>
<div class="faq-a">An amortisation schedule shows how each monthly bond repayment is split between interest and principal reduction. In the early years, most of your payment goes to interest — on a 20-year bond at 10.25%, roughly 86% of your first payment is interest and only 14% reduces what you owe. This ratio gradually shifts until the final payments are almost entirely principal. The schedule above shows your exact outstanding balance at every point in the loan.</div>
</div>
<div class="faq-item">
<button class="faq-q" onclick="toggleFAQ(this)">
Does paying extra into your bond save money?
<span class="faq-icon">+</span>
</button>
<div class="faq-a">Yes — significantly. Every extra rand paid into a South African bond reduces the outstanding principal immediately, which reduces the interest charged the following month. On a R1.5 million bond at 10.25% over 20 years, paying an extra R2,000/month saves approximately R420,000 in total interest and cuts the loan term by roughly 4.5 years. Toggle on "Show extra monthly payment savings" above to calculate your specific saving.</div>
</div>
<div class="faq-item">
<button class="faq-q" onclick="toggleFAQ(this)">
How does the SA prime rate affect my bond repayment?
<span class="faq-icon">+</span>
</button>
<div class="faq-a">Most South African home loans are variable-rate, meaning your monthly repayment moves with the prime lending rate. Prime is currently 10.25% (repo 6.75% + 3.5% bank margin), held at the March 2026 SARB MPC meeting. The next MPC decision is 28 May 2026. On a R1 million bond, a 0.5% rate increase adds approximately R330 to the monthly repayment. Use the rate sensitivity table in the results above to see exactly how rate movements affect your specific bond.</div>
</div>
<div class="faq-item">
<button class="faq-q" onclick="toggleFAQ(this)">
What is the difference between this calculator and the home loan calculator?
<span class="faq-icon">+</span>
</button>
<div class="faq-a">The <a href="home-loan-calculator.html">home loan calculator</a> starts from the property price and deposit to determine your loan amount, then calculates monthly repayment and total interest. This bond repayment calculator starts from the loan amount directly and focuses on the full amortisation schedule — showing exactly how your balance reduces month by month — plus the impact of extra payments and rate sensitivity analysis. Use the home loan calculator to size the bond, then this one to model the repayment detail.</div>
</div>
</div>
</div>
<!-- Related calculators -->
<div class="section">
<h2>Related calculators</h2>
<div class="cards-grid">
<a href="home-loan-calculator.html" class="rel-card">
<div class="rel-card-icon">🏠</div>
<div class="rel-card-title">Home Loan Calculator</div>
<div class="rel-card-desc">Size your bond from property price and deposit</div>
</a>
<a href="transfer-duty-calculator.html" class="rel-card">
<div class="rel-card-icon">🏛️</div>
<div class="rel-card-title">Transfer Duty Calculator</div>
<div class="rel-card-desc">SARS transfer duty on your property purchase</div>
</a>
<a href="paye-tax-calculator.html" class="rel-card">
<div class="rel-card-icon">💼</div>
<div class="rel-card-title">PAYE Tax Calculator</div>
<div class="rel-card-desc">Check your take-home pay before committing</div>
</a>
<a href="debt-consolidation-calculator.html" class="rel-card">
<div class="rel-card-icon">📉</div>
<div class="rel-card-title">Debt Consolidation</div>
<div class="rel-card-desc">See how existing debt affects your qualification</div>
</a>
</div>
</div>
<!-- Related guides -->
<div class="section">
<h2>Related guides</h2>
<div class="guide-links">
<a href="home-loans-hub.html" class="guide-link">How much home loan can I afford in South Africa?</a>
<a href="home-loans-hub.html" class="guide-link">First time home buyer guide South Africa 2026</a>
<a href="home-loans-hub.html" class="guide-link">Home loan interest rates South Africa 2026</a>
<a href="home-loans-hub.html" class="guide-link">How much deposit do I need for a home loan?</a>
</div>
</div>
<div class="disclaimer">
<strong>Disclaimer:</strong> All results are estimates based on the standard amortisation formula and the interest rate entered. Results assume a level-payment, fully amortising bond with monthly compounding. Actual repayments may differ due to bank-specific fees (bond initiation, monthly service fees), rounding conventions, variable-rate adjustments, and payment timing. Calcunomics is not a registered financial services provider. Always confirm repayment figures with your lender before making financial commitments.
</div>
</div>
<script>
/* === State === */
let monthlyData = [];
let annualData = [];
let currentView = 'annual';
let extraEnabled = false;
/* === Helpers === */
function fmtR(n) {
return 'R' + Math.round(Math.abs(n)).toLocaleString('en-ZA');
}
function fmtRd(n, d = 0) {
return 'R' + Math.abs(n).toLocaleString('en-ZA', { minimumFractionDigits: d, maximumFractionDigits: d });
}
/* === Extra payment toggle === */
function toggleExtra() {
extraEnabled = !extraEnabled;
document.getElementById('extra-track').classList.toggle('on', extraEnabled);
document.getElementById('extra-field').classList.toggle('visible', extraEnabled);
}
/* === Amortisation engine === */
function buildSchedule(principal, annualRate, termYears, extraMonthly, startYear) {
const r = (annualRate / 100) / 12;
const n = termYears * 12;
const minPayment = principal * (r * Math.pow(1 + r, n)) / (Math.pow(1 + r, n) - 1);
const payment = minPayment + (extraMonthly || 0);
let balance = principal;
const months = [];
let totalInterest = 0;
let totalPrincipal = 0;
let month = 0;
while (balance > 0.01 && month < n + 1) {
const interestCharge = balance * r;
let principalPayment = Math.min(payment - interestCharge, balance);
if (principalPayment < 0) principalPayment = 0;
const actualPayment = interestCharge + principalPayment;
balance = Math.max(0, balance - principalPayment);
totalInterest += interestCharge;
totalPrincipal += principalPayment;
month++;
const yr = startYear + Math.floor((month - 1) / 12);
const mo = ((month - 1) % 12) + 1;
months.push({ month, year: yr, mo, payment: actualPayment, interest: interestCharge, principal: principalPayment, balance });
if (balance < 0.01) break;
}
// Build annual summary
const annuals = [];
for (let y = 0; y < Math.ceil(months.length / 12); y++) {
const slice = months.slice(y * 12, (y + 1) * 12);
annuals.push({
label: 'Year ' + (y + 1) + ' (' + (startYear + y) + ')',
payment: slice.reduce((s, m) => s + m.payment, 0),
interest: slice.reduce((s, m) => s + m.interest, 0),
principal: slice.reduce((s, m) => s + m.principal, 0),
balance: slice[slice.length - 1].balance,
});
}
return { months, annuals, minPayment, totalInterest, totalPrincipal, actualMonths: month };
}
/* === Main calculate === */
function calculate() {
const principal = parseFloat(document.getElementById('loan-amount').value) || 0;
const rate = parseFloat(document.getElementById('interest-rate').value) || 10.25;
const term = parseFloat(document.getElementById('loan-term').value) || 20;
const startYear = parseInt(document.getElementById('start-year').value) || 2026;
const extra = extraEnabled ? (parseFloat(document.getElementById('extra-payment').value) || 0) : 0;
if (principal <= 0) return;
const { months, annuals, minPayment, totalInterest, totalPrincipal, actualMonths } =
buildSchedule(principal, rate, term, extra, startYear);
const totalRepaid = totalInterest + totalPrincipal;
const interestRatio = totalRepaid > 0 ? (totalInterest / totalRepaid) * 100 : 0;
const payoffYear = startYear + Math.floor((actualMonths - 1) / 12);
// Hero stats
document.getElementById('r-monthly').textContent = fmtR(minPayment);
document.getElementById('r-monthly-sub').textContent = `over ${term} years at ${rate}%`;
document.getElementById('r-total').textContent = fmtR(totalRepaid);
document.getElementById('r-interest').textContent = fmtR(totalInterest);
document.getElementById('r-interest-pct').textContent = Math.round(interestRatio) + '% of total cost';
document.getElementById('r-principal').textContent = fmtR(principal);
document.getElementById('r-ratio').textContent = Math.round(interestRatio) + '% interest';
document.getElementById('r-first-int').textContent = fmtR(months[0]?.interest || 0);
document.getElementById('r-payoff-yr').textContent = payoffYear;
// Balance bar
const principalPct = (totalPrincipal / totalRepaid) * 100;
document.getElementById('bar-principal').style.width = principalPct.toFixed(1) + '%';
document.getElementById('bar-interest').style.width = (100 - principalPct).toFixed(1) + '%';
document.getElementById('bar-label-right').textContent = Math.round(interestRatio) + '% goes to interest';
// Extra payment savings
if (extra > 0) {
const baseSchedule = buildSchedule(principal, rate, term, 0, startYear);
const interestSaved = baseSchedule.totalInterest - totalInterest;
const monthsSaved = baseSchedule.actualMonths - actualMonths;
const yrsSaved = Math.floor(monthsSaved / 12);
const mosSaved = monthsSaved % 12;
const savedLabel = yrsSaved > 0
? `${yrsSaved}yr${yrsSaved !== 1 ? 's' : ''} ${mosSaved > 0 ? mosSaved + 'mo' : ''}`
: `${monthsSaved} months`;
document.getElementById('s-interest').textContent = fmtR(interestSaved);
document.getElementById('s-months').textContent = savedLabel;
document.getElementById('s-payoff').textContent = payoffYear;
document.getElementById('savings-banner').classList.add('visible');
} else {
document.getElementById('savings-banner').classList.remove('visible');
}
// Rate sensitivity table
const baseRate = rate;
const scenarios = [
{ label: 'Rate cut 1% (SARB easing)', delta: -1 },
{ label: 'Rate cut 0.5%', delta: -0.5 },
{ label: 'Current rate (' + rate + '%)', delta: 0, current: true },
{ label: 'Rate hike 0.5%', delta: 0.5 },
{ label: 'Rate hike 1%', delta: 1 },
{ label: 'Rate hike 2%', delta: 2 },
];
const rateTbody = document.getElementById('rate-tbody');
rateTbody.innerHTML = scenarios.map(s => {
const r2 = baseRate + s.delta;
if (r2 <= 0) return '';
const { minPayment: mp2, totalInterest: ti2 } = buildSchedule(principal, r2, term, 0, startYear);
const diff = mp2 - minPayment;
const diffStr = diff === 0 ? '—' : (diff > 0 ? '+' + fmtR(diff) : '−' + fmtR(Math.abs(diff)));
const diffClass = diff > 0 ? 'up' : diff < 0 ? 'down' : '';
return `<tr class="${s.current ? 'current-rate' : ''}">
<td>${s.label}${s.current ? ' ✓' : ''}</td>
<td>${r2.toFixed(2)}%</td>
<td>${fmtR(mp2)}</td>
<td class="${diffClass}">${diffStr}</td>
<td>${fmtR(ti2)}</td>
</tr>`;
}).join('');
// Store schedule data
monthlyData = months;
annualData = annuals;
renderSchedule();
document.getElementById('results').classList.add('visible');
document.getElementById('results').scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}
/* === Render amortisation table === */
function setView(v) {
currentView = v;
document.getElementById('vt-annual').classList.toggle('active', v === 'annual');
document.getElementById('vt-monthly').classList.toggle('active', v === 'monthly');
renderSchedule();
}
function renderSchedule() {
const tbody = document.getElementById('amort-tbody');
if (currentView === 'annual') {
tbody.innerHTML = annualData.map(row => `
<tr class="year-row">
<td>${row.label}</td>
<td>${fmtR(row.payment)}</td>
<td class="principal-col">${fmtR(row.principal)}</td>
<td class="interest-col">${fmtR(row.interest)}</td>
<td class="balance-col">${row.balance < 1 ? 'R0' : fmtR(row.balance)}</td>
</tr>`).join('');
} else {
const MONTHS = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
tbody.innerHTML = monthlyData.map(row => `
<tr>
<td>${MONTHS[row.mo - 1]} ${row.year}</td>
<td>${fmtR(row.payment)}</td>
<td class="principal-col">${fmtR(row.principal)}</td>
<td class="interest-col">${fmtR(row.interest)}</td>
<td class="balance-col">${row.balance < 1 ? 'R0' : fmtR(row.balance)}</td>
</tr>`).join('');
}
}
/* === FAQ === */
function toggleFAQ(btn) {
const item = btn.closest('.faq-item');
const isOpen = item.classList.contains('open');
document.querySelectorAll('.faq-item').forEach(el => el.classList.remove('open'));
if (!isOpen) item.classList.add('open');
}
/* === Init === */
window.addEventListener('load', () => calculate());
</script>
</body>
</html>