Industry Guide

Roofing Contractor Quote Form: Build Roof Replacement Calculators

February 2026 · 12 min read

Roofing jobs are high-ticket, high-competition. The contractor who responds first with a ballpark estimate often wins the job. Here's how to build a quote form that gives homeowners instant pricing while capturing the details you need for an accurate proposal.

Roofing quotes traditionally require a site visit before any numbers are discussed. That's changing. Homeowners expect online estimates before they'll even schedule an inspection. Give them a number - even a range - and you're already ahead of contractors who say "we need to come out first."

The key is collecting enough information to generate a reasonable estimate: roof size, material preferences, pitch, current condition. With those inputs, you can calculate a range that's accurate enough to set expectations while leaving room for site-visit adjustments.

Project Type and Urgency

Start by understanding what the customer needs. A full replacement, repair, or just an inspection? And how urgent is it? Emergency leak repairs get prioritized differently than "thinking about replacing next year."

const projectSection = form.addSubform('project', {
    title: 'Project Details'
});

projectSection.addRow(row => {
    row.addRadioButton('projectType', {
        label: 'What type of project?',
        options: [
            { id: 'replacement', name: 'Full Roof Replacement' },
            { id: 'repair', name: 'Roof Repair' },
            { id: 'inspection', name: 'Inspection Only' },
            { id: 'new-construction', name: 'New Construction' }
        ],
        orientation: 'vertical',
        isRequired: true
    });
});

projectSection.addRow(row => {
    row.addDropdown('urgency', {
        label: 'How urgent is this project?',
        options: [
            { id: 'emergency', name: 'Emergency - Active leak' },
            { id: 'asap', name: 'ASAP - Within 2 weeks' },
            { id: 'soon', name: 'Soon - Within 1-2 months' },
            { id: 'planning', name: 'Planning ahead - 3+ months' }
        ],
        isRequired: true
    });
});

The urgency field is valuable for scheduling. "Emergency - Active leak" should trigger immediate callback. "Planning ahead" can go into a nurture sequence. This is lead routing built into the form.

Property Details

Roof size is the primary pricing driver. Most homeowners don't know exact square footage, so a slider with reasonable defaults works better than a text input. Stories matter for access - two-story homes cost more to roof.

const propertySection = form.addSubform('property', {
    title: 'Property Information'
});

propertySection.addRow(row => {
    row.addDropdown('propertyType', {
        label: 'Property Type',
        options: [
            { id: 'single-family', name: 'Single Family Home' },
            { id: 'townhouse', name: 'Townhouse' },
            { id: 'multi-family', name: 'Multi-Family (2-4 units)' },
            { id: 'apartment', name: 'Apartment Building (5+)' },
            { id: 'commercial', name: 'Commercial Building' }
        ]
    });
    row.addDropdown('stories', {
        label: 'Number of Stories',
        options: [
            { id: '1', name: '1 Story' },
            { id: '1.5', name: '1.5 Stories' },
            { id: '2', name: '2 Stories' },
            { id: '2.5', name: '2.5 Stories' },
            { id: '3+', name: '3+ Stories' }
        ]
    });
});

propertySection.addRow(row => {
    row.addSlider('roofSquareFootage', {
        label: 'Approximate Roof Size',
        min: 500,
        max: 5000,
        step: 100,
        defaultValue: 1500,
        unit: 'sq ft'
    });
});

propertySection.addRow(row => {
    row.addDropdown('roofPitch', {
        label: 'Roof Pitch (Steepness)',
        options: [
            { id: 'flat', name: 'Flat or Nearly Flat' },
            { id: 'low', name: 'Low Pitch (walkable)' },
            { id: 'medium', name: 'Medium Pitch (standard)' },
            { id: 'steep', name: 'Steep Pitch' },
            { id: 'very-steep', name: 'Very Steep (requires special equipment)' },
            { id: 'unknown', name: 'Not sure' }
        ]
    });
});

Roof pitch significantly affects labor costs. Steep roofs require safety equipment, slow down work, and increase material waste. A 12/12 pitch might add 50% to labor compared to a walkable 4/12.

Pro tip

Include "Not sure" options for technical questions like roof pitch. Many homeowners can't answer these accurately. Follow up during the site visit to verify and adjust the estimate.

Current Roof Assessment

For repair and replacement projects, understanding the current roof helps scope the work. What material is there now? How old is it? What problems are visible?

const currentRoofSection = form.addSubform('currentRoof', {
    title: 'Current Roof',
    isVisible: () => projectType.value() !== 'new-construction'
});

currentRoofSection.addRow(row => {
    row.addDropdown('currentMaterial', {
        label: 'Current Roofing Material',
        options: [
            { id: 'asphalt-3tab', name: 'Asphalt Shingles (3-tab)' },
            { id: 'asphalt-architectural', name: 'Asphalt Shingles (Architectural)' },
            { id: 'wood', name: 'Wood Shakes/Shingles' },
            { id: 'tile-clay', name: 'Clay Tile' },
            { id: 'tile-concrete', name: 'Concrete Tile' },
            { id: 'metal', name: 'Metal Roofing' },
            { id: 'slate', name: 'Slate' },
            { id: 'flat-membrane', name: 'Flat/Membrane Roof' },
            { id: 'unknown', name: 'Not sure' }
        ]
    });
    row.addInteger('roofAge', {
        label: 'Roof Age (years)',
        min: 0,
        max: 100,
        placeholder: 'Approximate'
    });
});

currentRoofSection.addRow(row => {
    row.addCheckboxList('currentIssues', {
        label: 'Current Issues (select all that apply)',
        options: [
            { id: 'leaking', name: 'Active leaks' },
            { id: 'missing-shingles', name: 'Missing/damaged shingles' },
            { id: 'sagging', name: 'Sagging or uneven areas' },
            { id: 'moss-algae', name: 'Moss/algae growth' },
            { id: 'gutter-granules', name: 'Granules in gutters' },
            { id: 'interior-damage', name: 'Interior water stains' },
            { id: 'storm-damage', name: 'Storm damage' }
        ],
        orientation: 'vertical'
    });
});

The issues checklist serves two purposes: it helps you understand the scope, and it educates the homeowner about what to look for. Someone who notices "granules in gutters" is seeing a roof near end of life.

Material Selection

For replacements and new construction, material choice is the second biggest pricing factor after size. Different materials have dramatically different costs - from $4/sqft for basic asphalt to $20+/sqft for slate.

const materialsSection = form.addSubform('materials', {
    title: 'Desired Materials',
    isVisible: () => projectType.value() === 'replacement' ||
                     projectType.value() === 'new-construction'
});

materialsSection.addRow(row => {
    row.addRadioButton('desiredMaterial', {
        label: 'Preferred Roofing Material',
        options: [
            { id: 'asphalt', name: 'Asphalt Shingles' },
            { id: 'metal', name: 'Metal Roofing' },
            { id: 'tile', name: 'Tile (Clay or Concrete)' },
            { id: 'slate', name: 'Slate' },
            { id: 'undecided', name: 'Need guidance' }
        ],
        orientation: 'vertical'
    });
});

// Asphalt tier selection
materialsSection.addRow(row => {
    row.addDropdown('asphaltTier', {
        label: 'Asphalt Shingle Grade',
        options: [
            { id: 'economy', name: 'Economy (25-year warranty)' },
            { id: 'standard', name: 'Standard (30-year warranty)' },
            { id: 'premium', name: 'Premium (50-year warranty)' },
            { id: 'designer', name: 'Designer/Luxury' }
        ],
        isVisible: () => desiredMaterial.value() === 'asphalt'
    });
});

// Metal type selection
materialsSection.addRow(row => {
    row.addDropdown('metalType', {
        label: 'Metal Roofing Style',
        options: [
            { id: 'standing-seam', name: 'Standing Seam' },
            { id: 'metal-shingles', name: 'Metal Shingles' },
            { id: 'corrugated', name: 'Corrugated' },
            { id: 'stone-coated', name: 'Stone-Coated Steel' }
        ],
        isVisible: () => desiredMaterial.value() === 'metal'
    });
});

Conditional fields keep the form focused. Someone who wants metal roofing sees metal-specific options. Someone choosing asphalt sees shingle grades. "Need guidance" captures leads who want consultation.

Real-Time Pricing

Here's the calculator logic that brings it together. Base price per square foot times roof size, adjusted for materials, pitch, and complexity.

// Pricing calculation
const basePricePerSqFt: Record<string, number> = {
    'asphalt': 4.50,
    'metal': 9.00,
    'tile': 12.00,
    'slate': 20.00,
    'undecided': 6.00 // Use mid-range for estimates
};

const asphaltMultipliers: Record<string, number> = {
    'economy': 0.8,
    'standard': 1.0,
    'premium': 1.4,
    'designer': 1.8
};

const pitchMultipliers: Record<string, number> = {
    'flat': 0.9,
    'low': 1.0,
    'medium': 1.0,
    'steep': 1.2,
    'very-steep': 1.5,
    'unknown': 1.1
};

const estimatedCost = form.computedValue(() => {
    const sqft = roofSquareFootage.value() ?? 1500;
    const material = desiredMaterial.value() ?? 'undecided';
    const pitch = roofPitch.value() ?? 'unknown';

    let basePrice = basePricePerSqFt[material] ?? 6.00;

    // Apply asphalt tier multiplier
    if (material === 'asphalt') {
        const tier = asphaltTier.value() ?? 'standard';
        basePrice *= asphaltMultipliers[tier] ?? 1.0;
    }

    // Apply pitch multiplier
    basePrice *= pitchMultipliers[pitch] ?? 1.0;

    // Multi-story premium
    const stories = stories.value();
    if (stories === '2.5' || stories === '3+') {
        basePrice *= 1.15;
    }

    return sqft * basePrice;
});

const pricingSection = form.addSubform('pricing', {
    title: 'Estimated Cost',
    isVisible: () => projectType.value() === 'replacement' ||
                     projectType.value() === 'new-construction'
});

pricingSection.addRow(row => {
    row.addPriceDisplay('lowEstimate', {
        label: 'Estimate Range',
        computedValue: () => estimatedCost() * 0.85,
        currency: '$',
        decimals: 0,
        prefix: 'From'
    });
    row.addPriceDisplay('highEstimate', {
        computedValue: () => estimatedCost() * 1.15,
        currency: '$',
        decimals: 0,
        prefix: 'To'
    });
});

Showing a range (±15%) sets appropriate expectations. Roofing has too many variables for a single number to be accurate before a site visit. The range tells customers "it'll be roughly in this ballpark" which is what they need to decide if they want to proceed.

Additional Work

Roofing projects often reveal additional needs. Rotted fascia, damaged soffits, inadequate ventilation. Collecting this upfront helps you scope the complete project rather than surprising customers with add-ons later.

const additionalSection = form.addSubform('additional', {
    title: 'Additional Work Needed'
});

additionalSection.addRow(row => {
    row.addCheckboxList('additionalWork', {
        label: 'Check all that may apply',
        options: [
            { id: 'gutters', name: 'Gutter replacement/repair' },
            { id: 'fascia', name: 'Fascia board repair' },
            { id: 'soffit', name: 'Soffit repair' },
            { id: 'skylights', name: 'Skylight installation/replacement' },
            { id: 'ventilation', name: 'Roof ventilation improvement' },
            { id: 'chimney', name: 'Chimney flashing/repair' },
            { id: 'insulation', name: 'Attic insulation' }
        ],
        orientation: 'vertical'
    });
});

additionalSection.addRow(row => {
    row.addCheckbox('needsPermit', {
        label: 'I need help with permits (if unsure, we can advise)'
    });
    row.addCheckbox('insuranceClaim', {
        label: 'This may be an insurance claim',
        isVisible: () => currentIssues.value()?.includes('storm-damage')
    });
});

The insurance claim checkbox only appears if they selected storm damage. This is valuable lead intelligence - insurance jobs have different sales processes and potentially higher margins.

See more contractor quote form examples in our gallery.

Contact and Location

Finally, capture contact information and property address. The address is particularly useful - you can check service area before responding and potentially view the roof on satellite imagery before the visit.

const contactSection = form.addSubform('contact', {
    title: 'Contact Information'
});

contactSection.addRow(row => {
    row.addTextbox('fullName', {
        label: 'Full Name',
        isRequired: true
    });
    row.addTextbox('phone', {
        label: 'Phone Number',
        isRequired: true,
        pattern: '^[0-9\-\+\(\)\s]+$'
    });
});

contactSection.addRow(row => {
    row.addEmail('email', {
        label: 'Email',
        isRequired: true
    });
});

contactSection.addRow(row => {
    row.addAddress('propertyAddress', {
        label: 'Property Address',
        isRequired: true,
        restrictToCountries: ['us'],
        showMap: true
    });
});

contactSection.addRow(row => {
    row.addDropdown('preferredContact', {
        label: 'Best Way to Reach You',
        options: [
            { id: 'phone', name: 'Phone Call' },
            { id: 'text', name: 'Text Message' },
            { id: 'email', name: 'Email' }
        ]
    });
    row.addDropdown('bestTime', {
        label: 'Best Time to Contact',
        options: [
            { id: 'morning', name: 'Morning (8am-12pm)' },
            { id: 'afternoon', name: 'Afternoon (12pm-5pm)' },
            { id: 'evening', name: 'Evening (5pm-8pm)' },
            { id: 'anytime', name: 'Anytime' }
        ]
    });
});

The map display confirms you have the right property. For roofing, getting the exact address matters - you might have multiple similar addresses on one street, especially with multi-unit properties.

Repair Estimates

Repair pricing works differently than replacement. It's more about the type of repair than square footage. Handle this with a separate calculation path for repair projects.

const repairPricing = form.addSubform('repairPricing', {
    title: 'Repair Estimate',
    isVisible: () => projectType.value() === 'repair'
});

const repairTypes: Record<string, { min: number; max: number }> = {
    'leaking': { min: 300, max: 1500 },
    'missing-shingles': { min: 150, max: 600 },
    'sagging': { min: 500, max: 3000 },
    'chimney': { min: 300, max: 800 },
    'flashing': { min: 200, max: 500 }
};

const repairEstimate = form.computedValue(() => {
    const issues = currentIssues.value() ?? [];
    if (issues.length === 0) return null;

    let minTotal = 0;
    let maxTotal = 0;

    issues.forEach(issue => {
        const pricing = repairTypes[issue];
        if (pricing) {
            minTotal += pricing.min;
            maxTotal += pricing.max;
        }
    });

    // Service call minimum
    minTotal = Math.max(minTotal, 250);

    return { min: minTotal, max: maxTotal };
});

repairPricing.addRow(row => {
    row.addPriceDisplay('repairLow', {
        label: 'Estimated Repair Cost',
        computedValue: () => repairEstimate()?.min ?? null,
        currency: '$',
        decimals: 0,
        prefix: 'From'
    });
    row.addPriceDisplay('repairHigh', {
        computedValue: () => repairEstimate()?.max ?? null,
        currency: '$',
        decimals: 0,
        prefix: 'To'
    });
});

Repair estimates stack based on selected issues. Active leak plus missing shingles plus chimney flashing? Add those ranges together. A minimum service call fee ensures small jobs are still profitable.

PDF Quote Generation

For a professional touch, generate a PDF quote that customers can reference. This works well for the higher estimate range - formal documentation builds trust for high-ticket projects.

form.configurePdf('quote', pdf => {
    pdf.configure({
        filename: 'roofing-estimate.pdf',
        allowUserDownload: true,
        downloadButtonLabel: 'Download Estimate',
        header: {
            title: 'Preliminary Roofing Estimate',
            subtitle: 'ABC Roofing Company'
        },
        footer: {
            text: 'This is a preliminary estimate. Final pricing subject to site inspection.',
            showPageNumbers: true
        }
    });

    pdf.addSection('Property', section => {
        section.addRow(row => {
            row.addField('Address', propertyAddress.value()?.formattedAddress ?? '');
            row.addField('Property Type', getPropertyTypeLabel());
        });
        section.addRow(row => {
            row.addField('Roof Size', `${roofSquareFootage.value()} sq ft`);
            row.addField('Stories', stories.value() ?? '');
        });
    });

    pdf.addSection('Project Details', section => {
        section.addRow(row => {
            row.addField('Project Type', getProjectTypeLabel());
            row.addField('Material', getMaterialLabel());
        });
    });

    pdf.addSection('Estimate', section => {
        section.addRow(row => {
            row.addField('Estimated Range',
                `$${formatNumber(estimatedCost() * 0.85)} - $${formatNumber(estimatedCost() * 1.15)}`);
        });
        section.addText('Final pricing will be provided after on-site inspection.');
    });
});

The PDF serves as a leave-behind even before the site visit. Customers can share it with spouses, compare with other quotes, and reference it when you call to schedule the inspection.

Putting It Together

A complete roofing quote form guides customers through project type, property details, material preferences, and contact information while calculating an estimate in real time. The form adapts based on answers - replacement projects see different questions than repairs.

The instant estimate is the hook. Customers get immediate value, which increases form completion rates. You get qualified leads with enough information to prioritize and prepare for site visits. Everyone wins.

Common Questions

How accurate should online roofing estimates be?

Aim for ±20% of final quote. Online estimates can't account for hidden damage, exact measurements, or access challenges. Frame it as a 'ballpark' that gives customers a sense of budget before you visit.

Should I show my actual pricing in the calculator?

Use representative pricing, not your exact numbers. The goal is setting expectations, not binding quotes. You can always adjust during the proposal after site inspection reveals actual conditions.

How do I handle commercial roofing requests?

Commercial roofing is different enough to warrant a separate form. Different materials (TPO, EPDM, modified bitumen), different pricing models (per roofing 'square'), different decision-makers. Build a dedicated commercial form rather than branching.

What about roof financing options?

Add a checkbox asking if they're interested in financing. Many homeowners can't pay $15-25k cash. Showing monthly payment options (if you offer financing) can make projects more accessible and increase conversions.

Ready to Build Your Roofing Quote Form?

Create calculators that win jobs by responding faster with instant estimates.