Manchester cheap man and van services

Manchester Man with a Van & House Removals Company

The Van Man Co.

We provide man and van and removals services across Manchester and the wider Greater Manchester area. Whether you're moving from a city centre apartment, relocating from a family home in the suburbs, or need help with furniture delivery, we connect you to local drivers who know the city and can help with moves of all sizes.

Get quotes from our removalists, or use our man and van booking form below for an instant price and to get booked in straight away. It only takes 30 seconds!

Manchester cheap man and van services

Manchester Man and Van & House Removals Company

The Van Man Co.

We provide man and van and removals services across Manchester and the wider Greater Manchester area. Whether you're moving from a city centre apartment, relocating from a family home in the suburbs, or need help with furniture delivery, we connect you to local drivers who know the city and can help with moves of all sizes.

Find our rates, van services, or use our quote estimator below for an instant price and to get booked in straight away. It only takes 30 seconds!

1. Enter your postcodes and details below

Pick up and drop off

2. Choose your driver and van size

Find a driver near you

3. Get an instant quote and book in

Get booked in – get stuff moved

1. Enter postcodes

2. Select a driver

3. Book in the job

Select A Moving Service

Service selection: choose the move you need to get started.
Getting a quote for: Man & Van
Example: 30 mins to load + 30 mins to unload = select 1 hour

Estimate

Enter details and click “Get a quote”.
Getting a quote for: Full Removals
Submitting Your Request
Please wait while we upload your files...

Tell us about the move

Share the key details so we can plan the route.

A quick walkthrough video helps us recommend the right van size and give you an accurate quote. Just a 1-minute phone video of each room showing what's coming and what's staying works perfectly.

Drag and drop or select videos/photos. Each file will stack below—remove any with the × button. Videos up to 50 MB. Got a bigger one? Send it to us on WhatsApp 0330 043 0885 and we'll attach it to your enquiry.

    Moving from and to

    Tell us about the property types and locations.

    Moving From

    Moving To

    How do we reach you?

    We’ll confirm availability after a quick review.

    so CSS rules can't // fail to apply and the modal is guaranteed to render. Returns a Promise: // true → customer tapped Continue, form submits (oversized videos skipped) // false → customer tapped Cancel, handler returns so they can edit function confirmOversizedVideos(oversized){ return new Promise(function(resolve){ if (!oversized.length) return resolve(true); const WA_URL = 'https://wa.me/443300430885?text=Hi%2C%20my%20removals%20video%20was%20too%20large%20to%20upload%20through%20your%20form%20%E2%80%94%20sending%20it%20here.'; // Backdrop const backdrop = document.createElement('div'); backdrop.setAttribute('role','dialog'); backdrop.setAttribute('aria-modal','true'); backdrop.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.7);z-index:99999;display:flex;align-items:center;justify-content:center;padding:20px;box-sizing:border-box;font-family:Poppins,system-ui,sans-serif;'; // Box const box = document.createElement('div'); box.style.cssText = 'background:#fff;padding:28px 28px 24px;border-radius:12px;max-width:440px;width:100%;box-sizing:border-box;box-shadow:0 20px 60px rgba(0,0,0,0.3);text-align:center;'; // Title const title = document.createElement('h3'); title.textContent = "Your video's too large to upload here"; title.style.cssText = 'margin:0 0 14px;font-size:18px;font-weight:600;color:#111;'; box.appendChild(title); // Lead text const lead = document.createElement('p'); lead.style.cssText = 'margin:0 0 12px;font-size:14px;line-height:1.5;color:#444;'; lead.textContent = "Videos up to 50 MB can go through the form. Anything bigger needs to come over WhatsApp — we'll attach it to your enquiry."; box.appendChild(lead); // File list const list = document.createElement('div'); list.style.cssText = 'margin:14px 0;padding:10px 12px;background:#f7f7f9;border-radius:8px;font-size:13px;color:#555;text-align:left;'; oversized.forEach(function(f){ const row = document.createElement('div'); const mb = (f.size/1048576).toFixed(1); row.textContent = f.name + ' — ' + mb + ' MB'; row.style.cssText = 'margin:2px 0;word-break:break-word;'; list.appendChild(row); }); box.appendChild(list); // Sub text — built from textContent to avoid any HTML/JS parse issues // when the file goes through WordPress / WPCode snippet processing. const sub = document.createElement('p'); sub.style.cssText = 'margin:0 0 18px;font-size:14px;line-height:1.5;color:#444;'; sub.textContent = "Tap Open WhatsApp below to send your video to us, then come back and tap Continue to send your enquiry. Your details and photos go through either way."; box.appendChild(sub); // Actions const actions = document.createElement('div'); actions.style.cssText = 'display:flex;flex-direction:column;gap:10px;'; const waBtn = document.createElement('a'); waBtn.href = WA_URL; waBtn.target = '_blank'; waBtn.rel = 'noopener'; waBtn.textContent = 'Open WhatsApp — 0330 043 0885'; waBtn.style.cssText = 'background:#25D366;color:#fff;padding:12px 18px;border-radius:8px;text-decoration:none;font-weight:600;font-size:14px;display:block;text-align:center;'; const continueBtn = document.createElement('button'); continueBtn.type = 'button'; continueBtn.textContent = 'Continue — send my enquiry'; continueBtn.style.cssText = 'background:#DF1AD1;color:#fff;border:0;padding:12px 18px;border-radius:8px;cursor:pointer;font-weight:600;font-size:14px;'; const cancelBtn = document.createElement('button'); cancelBtn.type = 'button'; cancelBtn.textContent = 'Cancel — let me edit'; cancelBtn.style.cssText = 'background:transparent;border:1px solid #d1d5db;color:#444;padding:10px 18px;border-radius:8px;cursor:pointer;font-size:13px;font-weight:500;'; actions.appendChild(waBtn); actions.appendChild(continueBtn); actions.appendChild(cancelBtn); box.appendChild(actions); backdrop.appendChild(box); document.body.appendChild(backdrop); function close(result){ try { document.body.removeChild(backdrop); } catch(e){} resolve(result); } continueBtn.addEventListener('click', function(){ close(true); }); cancelBtn.addEventListener('click', function(){ close(false); }); // Click outside the box also cancels backdrop.addEventListener('click', function(e){ if (e.target === backdrop) close(false); }); }); } if (uploadBox) { ['dragenter','dragover'].forEach(evt => { uploadBox.addEventListener(evt, (e) => { e.preventDefault(); e.stopPropagation(); uploadBox.classList.add('dragover'); }); }); ['dragleave','drop'].forEach(evt => { uploadBox.addEventListener(evt, (e) => { e.preventDefault(); e.stopPropagation(); uploadBox.classList.remove('dragover'); }); }); uploadBox.addEventListener('drop', (e) => { if (e.dataTransfer && e.dataTransfer.files.length) { addMedia(e.dataTransfer.files); } }); } function showStep(step) { document.querySelectorAll('.step').forEach(s => s.classList.remove('active')); const activeStep = document.getElementById('step' + step); activeStep.classList.add('active'); const errorMsg = document.getElementById('rmvErrorMsg'); if (errorMsg) errorMsg.classList.remove('show'); const progress = (step / 3) * 100; document.getElementById('rmvProgressFill').style.width = progress + '%'; document.getElementById('rmvProgressLabel').textContent = `Step ${step} of 3`; currentStep = step; // Smart scroll - scroll to service selector panel (like Man and Van scrolls to summaryEl) // On main site: scroll to tvmcRemovalsPanel or tvmcSelectorPanel container // In iframe: scroll to progressBar const inIframe = window.parent !== window; const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent); // Find scroll target: on main site use the panel container, in iframe use progressBar const scrollTarget = inIframe ? document.getElementById('rmvProgressBar') : (document.getElementById('tvmcRemovalsPanel') || document.getElementById('tvmcSelectorPanel') || document.getElementById('rmvProgressBar')); if (scrollTarget) { // Calculate absolute position from top of document let absTop = 0; let el = scrollTarget; while(el) { absTop += el.offsetTop; el = el.offsetParent; } // Always try scrollIntoView first try { scrollTarget.scrollIntoView({ behavior: isMobile ? 'instant' : 'smooth', block: 'start' }); } catch(e) { scrollTarget.scrollIntoView(true); } if (inIframe) { // In iframe: tell parent to scroll const sendScroll = () => { try { window.parent.postMessage({ type: 'tvmc-scroll-to', offsetY: absTop }, '*'); } catch(e) {} }; sendScroll(); setTimeout(sendScroll, 50); setTimeout(sendScroll, 100); setTimeout(sendScroll, 200); setTimeout(sendScroll, 400); setTimeout(sendScroll, 800); } else if (!window.PARTNER_ID) { // Main site: scroll again with header offset setTimeout(() => { window.scrollTo({ top: absTop - 150, behavior: 'smooth' }); }, 50); } } } function clearFieldError(field) { if (!field) return; field.classList.remove('has-error'); const error = field.querySelector('.req-error'); if (error && error.dataset.default) { error.textContent = error.dataset.default; } } function setFieldError(field, message) { if (!field) return; field.classList.add('has-error'); const error = field.querySelector('.req-error'); if (error) { const defaultMsg = error.dataset.default || error.textContent; error.textContent = message || defaultMsg || 'Please fill in this field.'; } } function bindClearOnInput(field, selectors = 'input, select, textarea') { if (!field) return; field.querySelectorAll(selectors).forEach(el => { el.addEventListener('input', () => clearFieldError(field)); el.addEventListener('change', () => clearFieldError(field)); }); } document.querySelectorAll('.field').forEach(field => bindClearOnInput(field)); document.getElementById('step1Next').addEventListener('click', () => { const selectedMoveType = document.querySelector('input[name="moveType"]:checked'); let hasError = false; if (!selectedMoveType) { if (moveTypeField) setFieldError(moveTypeField); hasError = true; } const packingService = document.querySelector('input[name="packingService"]:checked'); const packingServiceField = document.querySelector('input[name="packingService"]')?.closest('.field'); if (!packingService) { setFieldError(packingServiceField); hasError = true; } if (packingService && packingService.value === 'Yes') { const packingMaterials = document.querySelector('input[name="packingMaterials"]:checked'); const packingMaterialsField = document.getElementById('packingDetailsGroup'); if (!packingMaterials) { setFieldError(packingMaterialsField); hasError = true; } } const boxEstimate = document.getElementById('boxEstimate'); if (!boxEstimate.value) { setFieldError(boxEstimate.closest('.field')); hasError = true; } const bulkyItems = document.getElementById('bulkyItems'); if (!bulkyItems.value.trim()) { setFieldError(bulkyItems.closest('.field')); hasError = true; } if (hasError) { showError('Please fill in the required fields below.'); return; } showStep(2); }); document.getElementById('step2Back').addEventListener('click', () => showStep(1)); document.getElementById('step2Next').addEventListener('click', () => { const requiredFields = ['fromPropertySize', 'fromPostcode', 'toPropertySize', 'toPostcode']; let hasError = false; for (const id of requiredFields) { const el = document.getElementById(id); if (!el.value.trim()) { setFieldError(el.closest('.field')); hasError = true; } } const fromPropertyType = document.querySelector('input[name="fromPropertyType"]:checked'); const toPropertyType = document.querySelector('input[name="toPropertyType"]:checked'); if (!fromPropertyType) { setFieldError(document.querySelector('input[name="fromPropertyType"]')?.closest('.field')); hasError = true; } if (!toPropertyType) { setFieldError(document.querySelector('input[name="toPropertyType"]')?.closest('.field')); hasError = true; } const fromNeedsLift = fromPropertyType && fromPropertyType.value === 'Apartment/Flat'; if (fromNeedsLift) { const fromLift = document.querySelector('input[name="fromLift"]:checked'); const fromFloor = document.getElementById('fromFloor'); if (!fromLift) { setFieldError(document.querySelector('input[name="fromLift"]')?.closest('.field')); hasError = true; } if (!fromFloor.value.trim()) { setFieldError(fromFloor.closest('.field')); hasError = true; } } const toNeedsLift = toPropertyType && toPropertyType.value === 'Apartment/Flat'; if (toNeedsLift) { const toLift = document.querySelector('input[name="toLift"]:checked'); const toFloor = document.getElementById('toFloor'); if (!toLift) { setFieldError(document.querySelector('input[name="toLift"]')?.closest('.field')); hasError = true; } if (!toFloor.value.trim()) { setFieldError(toFloor.closest('.field')); hasError = true; } } const dateType = document.querySelector('input[name="movingDateType"]:checked'); if (dateType && dateType.value === 'Specific') { const specificDate = document.getElementById('specificDate'); if (!specificDate.value.trim()) { setFieldError(specificDate.closest('.field')); hasError = true; } } if (dateType && dateType.value === 'Flexible') { const flexibleFrom = document.getElementById('flexibleDateFrom'); const flexibleTo = document.getElementById('flexibleDateTo'); if (!flexibleFrom.value.trim() || !flexibleTo.value.trim()) { if (!flexibleFrom.value.trim()) { setFieldError(flexibleFrom.closest('.field')); } if (!flexibleTo.value.trim()) { setFieldError(flexibleTo.closest('.field')); } hasError = true; } } if (hasError) { showError('Please fill in the required fields below.'); return; } showStep(3); }); document.getElementById('step3Back').addEventListener('click', () => showStep(2)); function togglePropertyFields(prefix) { const propertyType = document.querySelector(`input[name="${prefix}PropertyType"]:checked`); const liftGroup = document.getElementById(`${prefix}LiftGroup`); const floorGroup = document.getElementById(`${prefix}FloorGroup`); const liftInputs = document.querySelectorAll(`input[name="${prefix}Lift"]`); const floorInput = document.getElementById(`${prefix}Floor`); if (!propertyType || propertyType.value !== 'Apartment/Flat') { if (liftGroup) liftGroup.style.display = 'none'; if (floorGroup) floorGroup.style.display = 'none'; liftInputs.forEach(input => input.checked = false); if (floorInput) floorInput.value = ''; return; } if (liftGroup) liftGroup.style.display = 'flex'; if (floorGroup) floorGroup.style.display = 'flex'; } ['from', 'to'].forEach(prefix => { const inputs = document.querySelectorAll(`input[name="${prefix}PropertyType"]`); inputs.forEach(input => { input.addEventListener('change', () => togglePropertyFields(prefix)); }); }); // togglePackingDetails moved above moveTypeCards handler — see earlier in file const packingServiceInputs2 = document.querySelectorAll('input[name="packingService"]'); packingServiceInputs2.forEach(input => input.addEventListener('change', togglePackingDetails)); const dateTypeInputs = document.querySelectorAll('input[name="movingDateType"]'); const specificDateField = document.getElementById('specificDateField'); const flexibleDateField = document.getElementById('flexibleDateField'); function toggleDateFields() { const selected = document.querySelector('input[name="movingDateType"]:checked'); const isFlexible = selected && selected.value === 'Flexible'; const isNotSure = selected && selected.value === 'Not sure'; if (specificDateField) specificDateField.style.display = isFlexible || isNotSure ? 'none' : 'block'; if (flexibleDateField) flexibleDateField.style.display = isFlexible && !isNotSure ? 'block' : 'none'; } dateTypeInputs.forEach(input => input.addEventListener('change', toggleDateFields)); toggleDateFields(); function initDatePicker(){ if (window.flatpickr) { flatpickr("#specificDate", { altInput:true, altFormat:"d/m/Y", dateFormat:"d/m/Y", disableMobile:true }); flatpickr("#flexibleDateFrom", { altInput:true, altFormat:"d/m/Y", dateFormat:"d/m/Y", disableMobile:true }); flatpickr("#flexibleDateTo", { altInput:true, altFormat:"d/m/Y", dateFormat:"d/m/Y", disableMobile:true }); } else { setTimeout(initDatePicker, 40); } } initDatePicker(); document.getElementById('removalsForm').addEventListener('submit', async (e) => { e.preventDefault(); const submitBtn = document.getElementById('submitBtn'); const successMsg = document.getElementById('rmvSuccessMsg'); const errorMsg = document.getElementById('rmvErrorMsg'); const thankYouMsg = document.getElementById('rmvThankYou'); const uploadingOverlay = document.getElementById('uploadingOverlay'); successMsg.classList.remove('show'); errorMsg.classList.remove('show'); thankYouMsg.classList.remove('show'); const nameField = document.getElementById('customerName'); const emailField = document.getElementById('customerEmail'); const phoneField = document.getElementById('customerPhone'); let hasError = false; if (!nameField.value.trim()) { setFieldError(nameField.closest('.field')); hasError = true; } if (!emailField.value.trim() || !emailField.checkValidity()) { setFieldError(emailField.closest('.field'), 'Please enter a valid email address.'); hasError = true; } if (!phoneField.value.trim()) { setFieldError(phoneField.closest('.field')); hasError = true; } if (hasError) { showError('Please fill in the required fields below.'); return; } // Submit-time gate: if any picked video exceeds the 50 MB Supabase Free // cap, show the custom modal with an Open-WhatsApp button. Lead is never // blocked silently — Continue submits (oversized skipped during upload); // Cancel returns to the form so the customer can edit. console.log('[Removals] Submit handler started'); const oversizedAtSubmit = mediaFiles.filter(isOversizedVideo); if (oversizedAtSubmit.length) { const proceed = await confirmOversizedVideos(oversizedAtSubmit); if (!proceed) { return; } } submitBtn.disabled = true; submitBtn.textContent = 'Processing...'; // Show uploading overlay uploadingOverlay.classList.add('show'); try { const moveTypeInput = document.querySelector('input[name="moveType"]:checked'); const moveTypeLabel = moveTypeInput ? moveTypeInput.closest('.crew-card').querySelector('.crew-title').textContent.trim() : 'Not specified'; // Generate a unique submission ID. Used as the Supabase folder name AND as // the lookup key on the Airtable record from the photo/video-uploaded webhooks. const submissionUuid = (window.crypto && crypto.randomUUID) ? crypto.randomUUID() : 'sub-' + Date.now() + '-' + Math.random().toString(36).slice(2, 10); // Split media: BOTH videos and photos now go to Supabase via TUS (resumable, // reliable on bad networks). Multipart was unreliable on poor connections — // a 58MB-video + 5MB-photos test from Vietnam lost ALL media because the // multipart body upload timed out before n8n could ack. // Oversized videos (> 50 MB Supabase Free cap) are excluded from the upload // queue — they're shown inline with a WhatsApp fallback so the lead is never // blocked by a video the platform can't accept. const allVideos = mediaFiles.filter(isVideoFile); const videos = allVideos.filter(function(f){ return !isOversizedVideo(f); }); const oversizedVideos = allVideos.filter(isOversizedVideo); const photos = mediaFiles.filter(function(f){ return !isVideoFile(f); }); const hasVideo = videos.length > 0; const hasPhotos = photos.length > 0; const hasOversizedVideo = oversizedVideos.length > 0; const movingDateType = (document.querySelector('input[name="movingDateType"]:checked') || {}).value || ''; // Build a tiny JSON body. ~5KB max — no file bytes. Files upload separately // to Supabase via TUS after the lead is safely captured in Airtable. const body = { booking_type: 'Removal', source: 'removals_public', timestamp: new Date().toISOString(), page_url: window.location.href, gclid: window.__tvmc_gclid || '', // Partner tracking (if form loaded in partner widget) partner_source: window.PARTNER_ID || '', partner_name: (window.PARTNER_CONFIG && window.PARTNER_CONFIG.company_name) || '', partner_stripe_account: (window.PARTNER_CONFIG && window.PARTNER_CONFIG.stripe_account_id) || '', partner_commission_type: (window.PARTNER_CONFIG && window.PARTNER_CONFIG.commission_type) || '', partner_commission_value: (window.PARTNER_CONFIG && window.PARTNER_CONFIG.commission_value) || '', // Customer details name: document.getElementById('customerName').value.trim(), email: document.getElementById('customerEmail').value.trim(), phone: document.getElementById('customerPhone').value.trim(), // Move details move_type: moveTypeLabel, service_selected: moveTypeLabel, packing_service: (document.querySelector('input[name="packingService"]:checked') || {}).value || '', packing_materials: (document.querySelector('input[name="packingMaterials"]:checked') || {}).value || '', packing_details: document.getElementById('packingDetails').value.trim(), from_property_type: (document.querySelector('input[name="fromPropertyType"]:checked') || {}).value || '', from_lift: (document.querySelector('input[name="fromLift"]:checked') || {}).value || '', from_floor: document.getElementById('fromFloor').value.trim(), from_property_size: document.getElementById('fromPropertySize').value, from_postcode: document.getElementById('fromPostcode').value.trim(), to_property_type: (document.querySelector('input[name="toPropertyType"]:checked') || {}).value || '', to_lift: (document.querySelector('input[name="toLift"]:checked') || {}).value || '', to_floor: document.getElementById('toFloor').value.trim(), to_property_size: document.getElementById('toPropertySize').value, to_postcode: document.getElementById('toPostcode').value.trim(), moving_date_type: movingDateType, specific_date: movingDateType === 'Specific' ? document.getElementById('specificDate').value : '', flexible_from: movingDateType === 'Flexible' ? document.getElementById('flexibleDateFrom').value : '', flexible_to: movingDateType === 'Flexible' ? document.getElementById('flexibleDateTo').value : '', bulky_items: document.getElementById('bulkyItems').value.trim(), box_estimate: document.getElementById('boxEstimate').value.trim(), moving_from_notes: document.getElementById('fromNotes').value.trim(), moving_to_notes: document.getElementById('toNotes').value.trim(), // Submission tracking — used by photo/video-uploaded webhooks to find the // Airtable record by submission_uuid and mirror Supabase files into Drive. submission_uuid: submissionUuid, has_video: hasVideo ? 'true' : 'false', has_photos: hasPhotos ? 'true' : 'false', // Customer was told their video is too large to upload here and to send // it via WhatsApp instead. Admin/n8n can use this to set expectations // ("a WhatsApp video is coming for this enquiry"). has_oversized_video: hasOversizedVideo ? 'true' : 'false', oversized_video_count: oversizedVideos.length }; // Phase 1: send the lead to n8n. Tiny JSON, completes in <1s on any // realistic connection. Retry up to 3x with exponential backoff in case of // a transient blip — payload is small enough that retries are cheap. let response = null; let lastErr = null; for (let attempt = 0; attempt < 3; attempt++) { try { response = await fetch(N8N_WEBHOOK, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) }); if (response.ok) { lastErr = null; break; } lastErr = new Error('Server returned ' + response.status); } catch (e) { lastErr = e; } if (attempt < 2) { await new Promise(function(r){ setTimeout(r, 1000 * Math.pow(2, attempt)); }); // 1s, 2s } } if (lastErr) throw lastErr; // From this point on the lead is safely in Airtable + Drive folder created. // Per-file upload errors below DO NOT trigger the catch block — we never // want to lose the lead just because one photo / video upload failed. const overlayTextEl = uploadingOverlay.querySelector('.uploading-text'); const overlaySubEl = uploadingOverlay.querySelector('.uploading-subtext'); // Phase 2a: photos via TUS to tvmc-removals-photos. Compression keeps // chunks small for faster uploads on poor connections (~10MB → ~500KB). // Falls back to original file on any compression failure. let compressedPhotos = photos; if (hasPhotos) { if (overlayTextEl) overlayTextEl.textContent = 'Preparing photos…'; if (overlaySubEl) overlaySubEl.textContent = 'Optimising image sizes for upload.'; try { compressedPhotos = await compressPhotosForUpload(photos); } catch (compressErr) { console.warn('[Removals] compress helper threw, using originals', compressErr); compressedPhotos = photos; } if (overlayTextEl) overlayTextEl.textContent = 'Uploading your photos…'; if (overlaySubEl) overlaySubEl.textContent = 'Please keep this tab open.'; for (let i = 0; i < compressedPhotos.length; i++) { const file = compressedPhotos[i]; const objectName = submissionUuid + '/' + file.name; try { await uploadFileViaTUS(file, objectName, submissionUuid, SUPABASE_PHOTOS_BUCKET, N8N_PHOTO_WEBHOOK, 'photo', function(sent, total){ const pct = total > 0 ? Math.min(99, Math.round((sent / total) * 100)) : 0; if (overlaySubEl) { overlaySubEl.textContent = compressedPhotos.length === 1 ? 'Uploading your photo — ' + pct + '%' : 'Uploading photo ' + (i + 1) + ' of ' + compressedPhotos.length + ' — ' + pct + '%'; } }); } catch (photoErr) { // One photo failed — don't stop the rest, don't lose the lead. console.warn('[Removals] Photo upload failed for', file.name, photoErr); } } } // Phase 2b: videos via TUS to tvmc-removals-videos. if (hasVideo) { if (overlayTextEl) overlayTextEl.textContent = 'Uploading your video…'; if (overlaySubEl) overlaySubEl.textContent = 'Please keep this tab open. Large videos can take a minute or two.'; for (let i = 0; i < videos.length; i++) { const file = videos[i]; const objectName = submissionUuid + '/' + file.name; try { await uploadVideoViaTUS(file, objectName, submissionUuid, function(sent, total){ const pct = total > 0 ? Math.min(99, Math.round((sent / total) * 100)) : 0; if (overlaySubEl) { overlaySubEl.textContent = videos.length === 1 ? 'Uploading your video — ' + pct + '%' : 'Uploading video ' + (i + 1) + ' of ' + videos.length + ' — ' + pct + '%'; } }); } catch (videoErr) { // One video failed — don't stop the rest, don't lose the lead. console.warn('[Removals] Video upload failed for', file.name, videoErr); } } } // Hide uploading overlay uploadingOverlay.classList.remove('show'); thankYouMsg.innerHTML = ` ✓ Your request has been sent. We're preparing your quote and will get back to you shortly. If we need anything else to firm up the numbers, we'll reach out right away.`; thankYouMsg.classList.add('show'); document.getElementById('rmvProgressBar').style.display = 'none'; document.getElementById('removalsForm').style.display = 'none'; // Scroll to thank you message const rect = thankYouMsg.getBoundingClientRect(); const top = window.scrollY + rect.top - 60; window.scrollTo({ top: Math.max(0, top), behavior: 'smooth' }); } catch (error) { // Hide uploading overlay on error uploadingOverlay.classList.remove('show'); errorMsg.textContent = error.message || 'An error occurred. Please try again.'; errorMsg.classList.add('show'); // Scroll to error message errorMsg.scrollIntoView({ behavior: 'smooth', block: 'start' }); } finally { submitBtn.disabled = false; submitBtn.textContent = 'SEND BOOKING REQUEST'; } }); /** * Upload a file (photo or video) to Supabase Storage using the TUS resumable protocol. * Resolves on success (or quiet failure of the Phase 3 notify), rejects on TUS error. * After the upload completes, notifies the supplied n8n webhook so it can * mirror the file from Supabase into the customer's Drive folder. * * Both photos (tvmc-removals-photos bucket) and videos (tvmc-removals-videos bucket) * use the same TUS endpoint — bucket selection is via the `bucketName` metadata. * * Reference: * - TUS protocol: https://tus.io/ * - Supabase resumable uploads: https://supabase.com/docs/guides/storage/uploads/resumable-uploads * - tus-js-client API: https://github.com/tus/tus-js-client/blob/main/docs/api.md */ function uploadFileViaTUS(file, objectName, submissionUuid, bucketName, notifyWebhookUrl, kind, onProgress) { return new Promise(function(resolve, reject) { if (!window.tus) { return reject(new Error('tus-js-client not loaded')); } var fallbackContentType = (kind === 'photo') ? 'image/jpeg' : 'video/mp4'; var upload = new tus.Upload(file, { endpoint: SUPABASE_TUS_ENDPOINT, retryDelays: [0, 1000, 3000, 5000, 10000, 30000, 60000], headers: { authorization: 'Bearer ' + SUPABASE_ANON_KEY, 'x-upsert': 'true' }, uploadDataDuringCreation: true, removeFingerprintOnSuccess: true, metadata: { bucketName: bucketName, objectName: objectName, contentType: file.type || fallbackContentType, cacheControl: '3600' }, // Supabase requires exactly 6 MB chunks (per docs). chunkSize: 6 * 1024 * 1024, onError: function(err) { reject(err); }, onProgress: function(sent, total) { try { if (typeof onProgress === 'function') onProgress(sent, total); } catch (e) {} }, onSuccess: function() { // Tell n8n the upload completed. Fire-and-forget — if the notify fails, // n8n still has the Airtable record with submission_uuid; an admin can // manually re-fire if needed. try { fetch(notifyWebhookUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ submission_uuid: submissionUuid, supabase_path: objectName }) }).catch(function() { console.warn('[Removals] Phase 3 ' + kind + ' notify failed; file is in Supabase under ' + objectName); }); } catch (e) { console.warn('[Removals] Phase 3 ' + kind + ' notify threw:', e); } resolve(); } }); upload.start(); }); } // Backwards-compat wrapper — preserves the existing video call site signature. function uploadVideoViaTUS(file, objectName, submissionUuid, onProgress) { return uploadFileViaTUS(file, objectName, submissionUuid, SUPABASE_BUCKET, N8N_VIDEO_WEBHOOK, 'video', onProgress); } })();

    Van Sizes & Rates in Manchester

    Hourly rates include the driver's round-trip commute to and from your job

    3 sizes

    Medium Van

    Ford Transit Custom or equivalent

    from£40.00/hr

    Internal dimensions 2.5m L × 1.7m W × 1.4m H
    Internal load space 6.0m³
    Medium van — Ford Transit Custom

    Large Van

    Ford Transit Van or equivalent

    from£45.00/hr

    Internal dimensions 3.4m L × 1.7m W × 1.8m H
    Internal load space 10.0m³
    Large van — Ford Transit

    Extra Large Van

    Ford Transit Luton or equivalent

    from£60.00/hr

    Internal dimensions 4.0m L × 2.0m W × 2.2m H
    Internal load space 18.0m³
    Extra large van — Ford Transit Luton

    Covering all areas of Manchester

    Local drivers across every neighbourhood

    Manchester is a major city with a mix of city centre apartments, Victorian terraces in the inner suburbs, and family housing across the wider Greater Manchester area. Whether you’re moving from a flat in the Northern Quarter or Ancoats, relocating from a house in Didsbury, Chorlton, or Sale, or downsizing to a smaller property in Levenshulme, we help you find local drivers who know the city and can assist with your move.

    We cover all types of moves across Manchester and the surrounding area. If you need a full house move, help with a flat clearance, assistance shifting furniture between properties, or someone to deliver appliances, we connect you to drivers with vans suited to the job. The city’s varied housing means moves range from compact city centre apartments in Salford Quays and Castlefield to larger Victorian terraces in Fallowfield and family homes in the suburbs of South Manchester.

    We also cover nearby areas including Liverpool, Bolton, Stockport, Oldham, and Salford. Manchester’s position on the M60 ring road and with excellent rail links including Piccadilly and Victoria stations makes it exceptionally well connected for moves across the North West. Local drivers regularly help with relocations around the city centre, the Manchester Royal Infirmary, and the retail areas near the Trafford Centre and Arndale. The busy streets around Oxford Road and Wilmslow Road see regular moving activity, while residential areas in Rusholme, Moss Side, and Hulme have steady demand. The university areas create student moving peaks in September and June, while the suburbs of Whalley Range, Withington, and Burnage attract families and professionals throughout the year.

    The wider Greater Manchester and North West region is easily accessible from the city centre. Drivers based here regularly help with moves across the conurbation and into neighbouring areas like Cheshire and Lancashire, whether you’re heading to a nearby town or relocating further afield. We make it straightforward to compare available drivers, choose the right van size, and find someone who suits your specific requirements.

    Here are some popular areas that we operate in around Manchester.

    Popular areas we cover in Manchester 58 areas

    Drivers Local To Manchester

    FAQ's

    note** make a text section that dynamically pulls in the drivers within 50 miles and links their profiles. 

    Then below have a link that anchors the user to the driver map.

    note ** put the long list of pills here for all suburbs within this Hub.

    ***Answer this Q at ChatGPT level – pull in with importer

    Reviews for Manchester

    5.0 3 reviews

    From drivers serving Manchester and nearby areas.

    • Khalidur 5 May 2026

      For Jake

    • Paul 29 Apr 2026

      Very good service very nice helpfull and good price thank you

      Items loaded into the van for Paul move
      Route from NG3 to NG3 NG3 → NG3

      For Tomasz

    • Titilope 20 Apr 2026

      Had a great experience. Tomasz was our driver and he was on time, efficient and very helpful. Couldn't recommend him enough. Spoke to the office multiple times before making my booking and they were very helpful and answered all my questions. Highly recommended.

      For Tomasz

    Get In Touch

    07460 293 731
    info@thevanmanco.co.uk

    Whatsapp