Concrete Footing Calculator

Professional concrete footing calculator for strip, pad, and pier footings. Get instant concrete volume, bag counts, weight, and cost estimates with an optional waste factor.

Footing inputs

Unit system
Footing type
ft
ft
ft

Add 0–15% to cover spillage, over-excavation, and subgrade variation.

$/yd^3 is used to estimate total cost.

How to use this calculator

This professional-grade concrete footing calculator helps builders, engineers, and DIYers estimate concrete volume, bag counts, weight, and optional cost for strip, pad, and pier footings. Enter dimensions that match the selected footing type, include a waste allowance, and optionally add a delivered price to see cost.

Results update immediately after you tap Calculate, so you can test multiple scenarios on site.

Methodology

The tool computes raw volume (L × W × T for strip and pad, segments for piers), applies the waste multiplier, and converts to both cubic feet and cubic yards (US) or cubic meters (Metric). Weight and bag counts rely on standard yields and densities noted in the citation section.

All calculations strictly follow the formulas and data referenced in the formulas section. Use the inputs to reflect your plans, and consult suppliers or engineers before placing material orders.

Glossary of variables

  • L: Footing length (strip or pad).
  • B: Width of the strip footing.
  • W: Width of the pad footing.
  • T: Thickness of the footing.
  • D: Pier diameter.
  • H: Pier height or depth.
  • N: Number of discrete footings (pads or piers).
  • w: Waste allowance, percent.
  • V: Calculated volume before waste.
  • Vtotal: Volume after waste.

How it works: step-by-step

Example: strip footing 80 ft long, 2.0 ft wide, 0.75 ft thick, with 10% waste.

  1. Base volume: V = 80 × 2.0 × 0.75 = 120 ft^3.
  2. Waste applied: Vtotal = 120 × (1 + 10/100) = 132 ft^3.
  3. Convert to cubic yards: 132 ÷ 27 ≈ 4.889 yd^3.
  4. 80 lb bags: ceil(132 / 0.60) = 220 bags.
  5. Weight: 132 × 150 = 19,800 lb.

If a supplier quotes $150/yd^3 delivered, cost ≈ 4.889 × 150 ≈ $733.35.

Frequently Asked Questions

Do I need to include the footing key or pedestal?

Yes. Include any additional volumes by increasing dimensions or summing separate estimates.

What if the trench is irregular?

Use average dimensions or break the footing into segments and sum the resulting volumes.

How do I switch between cubic yards and cubic meters?

Select the unit system toggle. US shows cubic yards as primary; Metric shows cubic meters. Both systems still show cubic feet to aid comparison.

Are the bag yields guaranteed?

No. Yields vary with compaction, water content, and product line. Verify with the product’s technical data sheet and round up.

Can I use this to size my footings?

No. This tool estimates concrete quantity only. Structural design must comply with codes such as ACI 318 and consider soil conditions.

What waste percentage should I use?

Use 5–10% for small projects and up to 15% for complex forms, odd shapes, or limited access. Lean on contractor experience to adjust.

Authorship and review

Verified by Ugo Candido on 2026-01-19.
Profile · LinkedIn

Editorial policy

CalcDomain content is created for educational purposes and reviewed for clarity, accuracy, and transparency. Inputs and assumptions are visible so you can verify how results are produced.
Formulas

Strip footing volume: V = L × B × T

Pad footing volume: V = N × (L × W × T)

Pier footing volume: V = N × π × (D / 2)2 × H

Waste factor: Vtotal = V × (1 + w / 100)

Conversions: 1 yd^3 = 27 ft^3, 1 m^3 = 35.3147 ft^3

Weight: WUS = Vft^3 × 150 lb/ft^3, WMetric = Vm^3 × 2400 kg/m^3

Bag counts: 40 lb = ceil(Vft^3 / 0.30), 60 lb = ceil(Vft^3 / 0.45), 80 lb = ceil(Vft^3 / 0.60), 25 kg = ceil(Vm^3 / 0.012)

Citations

QUIKRETE Company. Product data sheets and project calculator (2023–2024). Concrete yield per bag and mix guidance. https://www.quikrete.com/

ASTM C138/C138M-17a: Standard Test Method for Density, Yield, and Air Content of Concrete.

Portland Cement Association. Design and Control of Concrete Mixtures (16th ed., 2016).

National Institute of Standards and Technology — Engineering Laboratory (nist.gov, accessed 2026-01-19).

FEMA Building Science resources (fema.gov, accessed 2026-01-19).

Changelog

Version: 0.1.0-draft
Last code update: 2026-01-19

  • Initial audit spec draft generated from HTML extraction (review required).
  • Verify formulas match the calculator engine and convert text-only expressions to LaTeX.
  • Confirm sources are authoritative and relevant to the methodology.
Audit: Complete Verified by Ugo Candido Last updated 2026-01-19 Version 0.1.0-draft
Version 1.5.0
+ round(value, 2).toFixed(2); } }; const debounce = (fn, delay = 100) => { let timer; return (...args) => { clearTimeout(timer); timer = setTimeout(() => fn(...args), delay); }; }; function parseInputs() { const unitSystem = document.querySelector('input[name="unitSystem"]:checked').value; const footingType = document.querySelector('input[name="footingType"]:checked').value; return { unitSystem, footingType, stripLength: parseFloat(document.getElementById('stripLength').value), stripWidth: parseFloat(document.getElementById('stripWidth').value), stripDepth: parseFloat(document.getElementById('stripDepth').value), padLength: parseFloat(document.getElementById('padLength').value), padWidth: parseFloat(document.getElementById('padWidth').value), padDepth: parseFloat(document.getElementById('padDepth').value), padCount: parseFloat(document.getElementById('padCount').value), pierDiameter: parseFloat(document.getElementById('pierDiameter').value), pierHeight: parseFloat(document.getElementById('pierHeight').value), pierCount: parseFloat(document.getElementById('pierCount').value), wastePercentage: parseFloat(els.waste.value), price: parseFloat(els.price.value) }; } function validate(inputs) { const errors = []; if (!['us','metric'].includes(inputs.unitSystem)) { errors.push('Unit system is invalid.'); } if (!['strip','pad','pier'].includes(inputs.footingType)) { errors.push('Footing type is invalid.'); } const waste = inputs.wastePercentage; if (!Number.isFinite(waste) || waste < 0 || waste > 15) { errors.push('Waste allowance must be between 0 and 15%.'); } if (Number.isFinite(inputs.price) && inputs.price < 0) { errors.push('Price must be 0 or greater.'); } const measure = (value, name) => { if (!Number.isFinite(value) || value <= 0) { errors.push(`${name} must be greater than 0.`); } }; if (inputs.footingType === 'strip') { measure(inputs.stripLength, 'Total length'); measure(inputs.stripWidth, 'Width'); measure(inputs.stripDepth, 'Depth'); } else if (inputs.footingType === 'pad') { measure(inputs.padLength, 'Pad length'); measure(inputs.padWidth, 'Pad width'); measure(inputs.padDepth, 'Pad depth'); if (!Number.isFinite(inputs.padCount) || inputs.padCount < 1) { errors.push('Footing count must be at least 1.'); } } else if (inputs.footingType === 'pier') { measure(inputs.pierDiameter, 'Pier diameter'); measure(inputs.pierHeight, 'Pier height'); if (!Number.isFinite(inputs.pierCount) || inputs.pierCount < 1) { errors.push('Pier count must be at least 1.'); } } return { ok: errors.length === 0, errors }; } function compute(inputs) { const wasteMultiplier = 1 + (inputs.wastePercentage / 100); let baseFt = 0; let baseM = 0; if (inputs.unitSystem === 'us') { if (inputs.footingType === 'strip') { baseFt = inputs.stripLength * inputs.stripWidth * inputs.stripDepth; } else if (inputs.footingType === 'pad') { baseFt = inputs.padCount * inputs.padLength * inputs.padWidth * inputs.padDepth; } else { const radius = inputs.pierDiameter / 2; baseFt = inputs.pierCount * Math.PI * radius * radius * inputs.pierHeight; } baseM = baseFt / 35.3147; } else { if (inputs.footingType === 'strip') { baseM = inputs.stripLength * inputs.stripWidth * inputs.stripDepth; } else if (inputs.footingType === 'pad') { baseM = inputs.padCount * inputs.padLength * inputs.padWidth * inputs.padDepth; } else { const radius = inputs.pierDiameter / 2; baseM = inputs.pierCount * Math.PI * radius * radius * inputs.pierHeight; } baseFt = baseM * 35.3147; } if (!Number.isFinite(baseFt) || !Number.isFinite(baseM)) { return null; } const totalFt = baseFt * wasteMultiplier; const totalM = baseM * wasteMultiplier; const totalYd = totalFt / 27; const primaryVolume = inputs.unitSystem === 'us' ? totalYd : totalM; const weight = inputs.unitSystem === 'us' ? totalFt * 150 : totalM * 2400; const priceValue = Number.isFinite(inputs.price) ? inputs.price : null; const cost = Number.isFinite(priceValue) ? (inputs.unitSystem === 'us' ? totalYd * priceValue : totalM * priceValue) : null; const afterWasteFt = totalFt; const afterWasteM = totalM; const bags40 = Math.max(0, Math.ceil(afterWasteFt / 0.30)); const bags60 = Math.max(0, Math.ceil(afterWasteFt / 0.45)); const bags80 = Math.max(0, Math.ceil(afterWasteFt / 0.60)); const bags25kg = Math.max(0, Math.ceil(afterWasteM / 0.012)); return { baseFt, baseM, totalFt, totalM, totalYd, primaryVolume, weight, cost, bags40, bags60, bags80, bags25kg }; } function buildScheduleRows(outputs, inputs) { const primaryLabel = inputs.unitSystem === 'us' ? 'yd^3' : 'm^3'; const primaryValue = inputs.unitSystem === 'us' ? formatNumber(outputs.totalFt / 27, 3) + ' yd^3' : formatNumber(outputs.totalM, 3) + ' m^3'; return [ { step: 'Base volume (before waste)', ft: formatNumber(outputs.baseFt, 3), yd: formatNumber(outputs.baseFt / 27, 3), m: formatNumber(outputs.baseM, 3), notes: 'Raw footprint volume.' }, { step: 'Volume after waste', ft: formatNumber(outputs.totalFt, 2), yd: formatNumber(outputs.totalFt / 27, 3), m: formatNumber(outputs.totalM, 3), notes: `${primaryValue}` }, { step: 'Estimated weight', ft: '—', yd: '—', m: '—', notes: inputs.unitSystem === 'us' ? `${formatNumber(outputs.weight, 0)} lb` : `${formatNumber(outputs.weight, 0)} kg` }, { step: 'Bag counts (rounded up)', ft: '—', yd: '—', m: '—', notes: `40 lb: ${outputs.bags40}, 60 lb: ${outputs.bags60}, 80 lb: ${outputs.bags80}, 25 kg: ${outputs.bags25kg}` } ]; } function format(outputs, inputs) { const primaryLabel = inputs.unitSystem === 'us' ? 'yd^3' : 'm^3'; const secondaryLabel = 'ft^3'; return { primaryVolume: `${formatNumber(inputs.unitSystem === 'us' ? outputs.totalYd : outputs.totalM, 3)} ${primaryLabel}`, secondaryVolume: `${formatNumber(outputs.totalFt, 2)} ${secondaryLabel}`, weight: `${formatNumber(outputs.weight, 0)} ${inputs.unitSystem === 'us' ? 'lb' : 'kg'}`, cost: outputs.cost !== null ? formatCurrency(outputs.cost) : '—', bags40: outputs.bags40.toLocaleString(), bags60: outputs.bags60.toLocaleString(), bags80: outputs.bags80.toLocaleString(), bags25kg: outputs.bags25kg.toLocaleString(), scheduleRows: buildScheduleRows(outputs, inputs) }; } function renderSchedule() { els.scheduleBody.innerHTML = ''; if (!scheduleRows.length) { const tr = document.createElement('tr'); const td = document.createElement('td'); td.setAttribute('colspan', '5'); td.className = 'text-center text-sm text-gray-500'; td.textContent = 'No breakdown available.'; tr.appendChild(td); els.scheduleBody.appendChild(tr); return; } const fragment = document.createDocumentFragment(); scheduleRows.forEach((row) => { const tr = document.createElement('tr'); ['step','ft','yd','m','notes'].forEach((key) => { const td = document.createElement('td'); td.textContent = row[key] || '—'; tr.appendChild(td); }); fragment.appendChild(tr); }); els.scheduleBody.appendChild(fragment); } function render(formatted, errors) { if (!errors || errors.length === 0) { els.errorBox.style.display = 'none'; els.errorBox.textContent = ''; } else { els.errorBox.style.display = 'block'; els.errorBox.textContent = errors.join(' '); setPlaceholders(); els.downloadCsv.disabled = true; return; } if (!formatted) { setPlaceholders(); els.downloadCsv.disabled = true; return; } els.primaryVolume.textContent = formatted.primaryVolume; els.secondaryVolume.textContent = formatted.secondaryVolume; els.weightValue.textContent = formatted.weight; els.bags40.textContent = formatted.bags40; els.bags60.textContent = formatted.bags60; els.bags80.textContent = formatted.bags80; els.bags25kg.textContent = formatted.bags25kg; els.costEstimate.textContent = formatted.cost; scheduleRows = formatted.scheduleRows; renderSchedule(); els.downloadCsv.disabled = scheduleRows.length === 0; } function setPlaceholders() { els.primaryVolume.textContent = '—'; els.secondaryVolume.textContent = '—'; els.weightValue.textContent = '—'; els.bags40.textContent = '—'; els.bags60.textContent = '—'; els.bags80.textContent = '—'; els.bags25kg.textContent = '—'; els.costEstimate.textContent = '—'; } function updateFieldVisibility() { const selected = document.querySelector('input[name="footingType"]:checked').value; fieldSets.forEach((set) => { const isActive = set.dataset.type === selected || !set.dataset.type; set.hidden = !isActive; set.setAttribute('aria-hidden', (!isActive).toString()); }); } function refreshDimensionLabels(unit) { const unitLabel = unit === 'us' ? 'ft' : 'm'; dimensionSpans.forEach((span) => { if (span) span.textContent = unitLabel; }); els.priceUnit.textContent = unit === 'us' ? '$/yd^3' : '$/m^3'; } function refreshChips() { chips.forEach((chip) => { const input = chip.querySelector('input'); if (!input) return; chip.classList.toggle('selected', input.checked); }); } function resetInputs() { Object.entries(defaults).forEach(([id, value]) => { const el = document.getElementById(id); if (el) el.value = value; }); document.querySelector('input[name="unitSystem"][value="' + defaults.unitSystem + '"]').checked = true; document.querySelector('input[name="footingType"][value="' + defaults.footingType + '"]').checked = true; updateFieldVisibility(); refreshDimensionLabels(defaults.unitSystem); refreshChips(); } function update() { const inputs = parseInputs(); const validation = validate(inputs); if (!validation.ok) { render(null, validation.errors); return; } const outputs = compute(inputs); if (!outputs) { render(null, ['Calculation failed for the current inputs.']); return; } const formatted = format(outputs, inputs); render(formatted, []); } els.unitRadios.forEach((radio) => { radio.addEventListener('change', () => { refreshDimensionLabels(radio.value); debouncedUpdate(); refreshChips(); }); }); els.typeRadios.forEach((radio) => { radio.addEventListener('change', () => { updateFieldVisibility(); debouncedUpdate(); refreshChips(); }); }); element.addEventListener('change', debouncedUpdate); }); els.calcBtn.addEventListener('click', update); els.resetBtn.addEventListener('click', () => { resetInputs(); const debouncedUpdate = debounce(update, 100); document.querySelectorAll('#inputsCard input, #inputsCard select, #inputsCard textarea') .forEach((el) => { el.addEventListener('input', debouncedUpdate); el.addEventListener('change', debouncedUpdate); }); update(); }); els.toggleSchedule.addEventListener('click', () => { scheduleVisible = !scheduleVisible; els.scheduleWrap.style.display = scheduleVisible ? 'block' : 'none'; els.toggleSchedule.textContent = scheduleVisible ? 'Hide detailed breakdown' : 'View detailed breakdown'; }); els.downloadCsv.addEventListener('click', () => { if (!scheduleRows.length) return; let csv = 'Step,ft^3,yd^3,m^3,Notes\n'; scheduleRows.forEach((row) => { const values = [row.step, row.ft, row.yd, row.m, row.notes]; csv += values.map((value) => '"' + value.replace(/"/g, '""') + '"').join(',') + '\n'; }); const blob = new Blob([csv], { type: 'text/csv' }); const url = URL.createObjectURL(blob); const anchor = document.createElement('a'); anchor.href = url; anchor.download = 'concrete-footing-breakdown.csv'; anchor.click(); URL.revokeObjectURL(url); }); if (els.scheduleWrap) { els.scheduleWrap.style.display = 'none'; } refreshDimensionLabels(defaults.unitSystem); refreshChips(); updateFieldVisibility(); update(); })();