Address Verification
Compare user-entered address data against Google's authoritative address information to detect typos, abbreviations, and missing components.
Live Example
Enter Your Address
Verification Results
Enter your address and click "Verify Address" to see results
How It Works
Manual Entry
User types their address into individual fields (street, city, postcode, etc.)
Verification Search
Click "Verify Address" to search and select their address using Google Places
Compare & Correct
System highlights discrepancies and suggests corrections with one-click acceptance
Verification Features
Typo Detection
Fuzzy matching identifies spelling mistakes
Abbreviation Expansion
Recognises "St" vs "Street", "Rd" vs "Road"
Missing Component Detection
Identifies fields left blank that should be filled
Postcode Validation
Verifies UK postcode format and accuracy
One-Click Corrections
Accept individual suggestions or all at once
Verification Score
Visual percentage showing address accuracy
Common Use Cases
🛒 E-commerce Checkout
Prevent delivery failures by ensuring customers enter accurate addresses before order completion
📋 Account Registration
Maintain clean user databases by verifying addresses during sign-up
🏥 Insurance Forms
Critical for policy accuracy where incorrect addresses can cause claim issues
🔒 Fraud Prevention
Detect suspicious address patterns or non-existent addresses during verification
Implementation Code
Here's the complete implementation using vanilla JavaScript:
<script>
import { PlacesAutocomplete } from 'places-autocomplete-js';
document.addEventListener('DOMContentLoaded', () => {
let autocomplete;
let googleAddress = null;
let comparisons = [];
let verificationScore = 0;
// Manual address form data
const manualAddress = {
streetNumber: '',
streetName: '',
flat: '',
city: '',
county: '',
postcode: '',
country: 'United Kingdom'
};
// Normalise text for comparison
function normalise(text) {
return text.toLowerCase()
.replace(/[.,\/#!$%\^&\*;:{}=\-_`~()]/g, '')
.trim();
}
// Calculate Levenshtein distance for fuzzy matching
function levenshteinDistance(str1, str2) {
const matrix = [];
for (let i = 0; i <= str2.length; i++) {
matrix[i] = [i];
}
for (let j = 0; j <= str1.length; j++) {
matrix[0][j] = j;
}
for (let i = 1; i <= str2.length; i++) {
for (let j = 1; j <= str1.length; j++) {
if (str2.charAt(i - 1) === str1.charAt(j - 1)) {
matrix[i][j] = matrix[i - 1][j - 1];
} else {
matrix[i][j] = Math.min(
matrix[i - 1][j - 1] + 1,
matrix[i][j - 1] + 1,
matrix[i - 1][j] + 1
);
}
}
}
return matrix[str2.length][str1.length];
}
// Expand common abbreviations
function expandAbbreviations(text) {
const abbreviations = {
'st': 'street', 'rd': 'road', 'ave': 'avenue',
'dr': 'drive', 'ln': 'lane', 'ct': 'court',
'pl': 'place', 'blvd': 'boulevard', 'pk': 'park',
'sq': 'square', 'ter': 'terrace', 'wy': 'way'
};
let expanded = text.toLowerCase();
Object.keys(abbreviations).forEach(abbr => {
const regex = new RegExp(`\\b${abbr}\\b`, 'gi');
expanded = expanded.replace(regex, abbreviations[abbr]);
});
return expanded;
}
// Compare two address values
function compareValues(userValue, googleValue) {
if (!userValue && !googleValue) return 'match';
if (!userValue || !googleValue) return 'mismatch';
const normUser = normalise(userValue);
const normGoogle = normalise(googleValue);
// Exact match
if (normUser === normGoogle) return 'match';
// Check with abbreviations expanded
const expandedUser = expandAbbreviations(normUser);
const expandedGoogle = expandAbbreviations(normGoogle);
if (expandedUser === expandedGoogle) return 'partial';
// Fuzzy match for typos (80% similarity threshold)
const distance = levenshteinDistance(normUser, normGoogle);
const maxLength = Math.max(normUser.length, normGoogle.length);
const similarity = 1 - (distance / maxLength);
if (similarity > 0.8) return 'partial';
return 'mismatch';
}
// Parse Google address components
function parseGoogleAddress(addressComponents) {
const mapped = addressComponents.reduce((acc, component) => {
const type = component.types[0];
acc[type] = component.longText;
return acc;
}, {});
return {
streetNumber: mapped.street_number || '',
streetName: mapped.route || '',
flat: mapped.subpremise || '',
city: mapped.postal_town || mapped.locality || '',
county: mapped.administrative_area_level_2 ||
mapped.administrative_area_level_1 || '',
postcode: mapped.postal_code || '',
country: mapped.country || ''
};
}
// Perform verification
function performVerification() {
if (!googleAddress) return;
const parsed = parseGoogleAddress(googleAddress.addressComponents);
const fields = [
{ field: 'streetNumber', label: 'Street Number' },
{ field: 'streetName', label: 'Street Name' },
{ field: 'flat', label: 'Flat/Unit' },
{ field: 'city', label: 'City' },
{ field: 'county', label: 'County' },
{ field: 'postcode', label: 'Postcode' },
{ field: 'country', label: 'Country' }
];
comparisons = [];
let matches = 0;
fields.forEach(({ field, label }) => {
const userValue = manualAddress[field] || '';
const googleValue = parsed[field] || '';
let status;
let suggestion;
if (!userValue && googleValue) {
status = 'missing';
suggestion = `Add: ${googleValue}`;
} else if (userValue && !googleValue) {
status = 'match';
} else {
status = compareValues(userValue, googleValue);
if (status === 'partial' || status === 'mismatch') {
suggestion = googleValue;
}
}
if (status === 'match') matches++;
comparisons.push({
field,
label,
userValue,
googleValue,
status,
suggestion
});
});
// Calculate verification score
const totalFields = fields.filter(f =>
manualAddress[f.field] || parsed[f.field]
).length;
verificationScore = totalFields > 0
? Math.round((matches / totalFields) * 100)
: 0;
renderResults();
}
// Render verification results
function renderResults() {
const container = document.getElementById('results');
if (!googleAddress) {
container.innerHTML = `
<div class="empty-state">
<p>Enter your address and click "Verify Address" to see results</p>
</div>
`;
return;
}
const scoreColor = verificationScore >= 80 ? '#10b981' :
verificationScore >= 50 ? '#f59e0b' : '#ef4444';
let html = `
<div class="verification-score">
<div class="score-header">
<span>Verification Score</span>
<span class="score-value">${verificationScore}%</span>
</div>
<div class="score-bar">
<div class="score-fill" style="width: ${verificationScore}%; background: ${scoreColor}"></div>
</div>
</div>
`;
if (verificationScore < 100) {
html += `
<div class="alert alert-warning">
⚠️ Some discrepancies detected. Review suggestions below.
</div>
<button onclick="acceptAllSuggestions()" class="btn btn-success">
✓ Accept All Google Suggestions
</button>
`;
} else {
html += `
<div class="alert alert-success">
✓ Address verified successfully!
</div>
`;
}
html += '<div class="comparisons">';
comparisons.forEach(comp => {
if (!comp.userValue && !comp.googleValue) return;
const statusIcons = {
match: '✓',
partial: '⚠',
mismatch: '✗',
missing: 'ℹ'
};
const statusColors = {
match: '#10b981',
partial: '#f59e0b',
mismatch: '#ef4444',
missing: '#3b82f6'
};
const statusLabels = {
match: 'Verified',
partial: 'Typo/Abbreviation',
mismatch: 'Mismatch',
missing: 'Missing'
};
html += `
<div class="comparison-item">
<div class="comparison-header">
<span class="field-label">${comp.label}</span>
<span class="status-badge" style="background: ${statusColors[comp.status]}20; color: ${statusColors[comp.status]}">
${statusIcons[comp.status]} ${statusLabels[comp.status]}
</span>
</div>
<div class="comparison-values">
<div class="user-value">
<span class="value-label">Your Entry</span>
<span class="value-text">${comp.userValue || '—'}</span>
</div>
${comp.status !== 'match' && comp.googleValue ? `
<div class="google-suggestion">
<span class="value-label">Google Suggests</span>
<span class="value-text">${comp.googleValue}</span>
<button
onclick="acceptSuggestion('${comp.field}', '${comp.googleValue}')"
class="btn-accept"
>
Accept
</button>
</div>
` : ''}
</div>
</div>
`;
});
html += '</div>';
container.innerHTML = html;
}
// Accept all suggestions
window.acceptAllSuggestions = function() {
comparisons.forEach(comp => {
if (comp.googleValue) {
manualAddress[comp.field] = comp.googleValue;
document.getElementById(comp.field).value = comp.googleValue;
}
});
performVerification();
};
// Accept single suggestion
window.acceptSuggestion = function(field, value) {
manualAddress[field] = value;
document.getElementById(field).value = value;
performVerification();
};
// Open verification modal
function openVerificationModal() {
const hasData = Object.values(manualAddress).some(v => v.trim() !== '');
if (!hasData) {
alert('Please enter some address details first');
return;
}
document.getElementById('modal').classList.remove('hidden');
setTimeout(() => initAutocomplete(), 100);
}
// Close modal
function closeModal() {
document.getElementById('modal').classList.add('hidden');
if (autocomplete) {
autocomplete.destroy();
}
}
// Initialise autocomplete
function initAutocomplete() {
const container = document.getElementById('verification-autocomplete');
if (!container) return;
if (autocomplete) {
autocomplete.destroy();
}
autocomplete = new PlacesAutocomplete({
containerId: 'verification-autocomplete',
googleMapsApiKey: 'YOUR_GOOGLE_MAPS_API_KEY',
onResponse: (placeDetails) => {
googleAddress = placeDetails;
performVerification();
const alert = document.getElementById('modal-alert');
alert.classList.remove('hidden');
alert.innerHTML = '✓ Address selected. Check results on the form.';
},
onError: (error) => {
console.error('Autocomplete Error:', error.message || error);
},
requestParams: {
region: 'GB',
includedRegionCodes: ['GB']
},
fetchFields: ['formattedAddress', 'addressComponents'],
options: {
placeholder: 'Search for your address...',
clear_input: false
}
});
}
// Bind form inputs
['streetNumber', 'streetName', 'flat', 'city', 'county', 'postcode'].forEach(field => {
const input = document.getElementById(field);
input.addEventListener('input', (e) => {
manualAddress[field] = e.target.value;
});
});
// Event listeners
document.getElementById('verify-btn').addEventListener('click', openVerificationModal);
document.getElementById('close-modal').addEventListener('click', closeModal);
document.getElementById('reset-btn').addEventListener('click', () => {
Object.keys(manualAddress).forEach(key => {
manualAddress[key] = key === 'country' ? 'United Kingdom' : '';
const input = document.getElementById(key);
if (input) input.value = manualAddress[key];
});
googleAddress = null;
comparisons = [];
verificationScore = 0;
renderResults();
});
// Initial render
renderResults();
});
</script>
<!-- Manual Entry Form -->
<div class="form-container">
<h3>Enter Your Address</h3>
<div class="form-row">
<div class="form-group">
<label>Street Number</label>
<input type="text" id="streetNumber" placeholder="10">
</div>
<div class="form-group">
<label>Flat/Unit</label>
<input type="text" id="flat" placeholder="Flat 2A">
</div>
</div>
<div class="form-group">
<label>Street Name</label>
<input type="text" id="streetName" placeholder="Downing Street">
</div>
<div class="form-group">
<label>City</label>
<input type="text" id="city" placeholder="Westminster">
</div>
<div class="form-group">
<label>County</label>
<input type="text" id="county" placeholder="Greater London">
</div>
<div class="form-row">
<div class="form-group">
<label>Postcode</label>
<input type="text" id="postcode" placeholder="SW1A 2AA">
</div>
<div class="form-group">
<label>Country</label>
<input type="text" id="country" value="United Kingdom" disabled>
</div>
</div>
<div class="form-actions">
<button id="verify-btn" class="btn btn-primary">📍 Verify Address</button>
<button id="reset-btn" class="btn btn-secondary">Reset</button>
</div>
</div>
<!-- Results Container -->
<div id="results" class="results-container"></div>
<!-- Verification Modal -->
<div id="modal" class="modal hidden">
<div class="modal-content">
<h3>Verify Your Address</h3>
<p class="modal-description">
Search for your address using Google Places to verify your entry
</p>
<div id="verification-autocomplete"></div>
<div id="modal-alert" class="alert alert-success hidden"></div>
<div class="modal-footer">
<button id="close-modal" class="btn btn-secondary">Close</button>
</div>
</div>
</div>
<style>
.form-container {
padding: 1.5rem;
background: white;
border-radius: 0.5rem;
border: 1px solid #e5e7eb;
margin-bottom: 1.5rem;
}
.form-container h3 {
margin-bottom: 1rem;
font-size: 1.25rem;
font-weight: 600;
}
.form-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1rem;
}
.form-group {
margin-bottom: 1rem;
}
.form-group label {
display: block;
font-weight: 500;
margin-bottom: 0.5rem;
font-size: 0.875rem;
}
.form-group input {
width: 100%;
padding: 0.5rem;
border: 1px solid #d1d5db;
border-radius: 0.375rem;
font-size: 0.875rem;
}
.form-group input:disabled {
background: #f3f4f6;
cursor: not-allowed;
}
.form-actions {
display: flex;
gap: 0.75rem;
margin-top: 1rem;
}
.btn {
padding: 0.5rem 1rem;
border-radius: 0.375rem;
border: none;
cursor: pointer;
font-weight: 500;
font-size: 0.875rem;
}
.btn-primary {
background: #3b82f6;
color: white;
flex: 1;
}
.btn-primary:hover {
background: #2563eb;
}
.btn-secondary {
background: #f3f4f6;
color: #374151;
}
.btn-secondary:hover {
background: #e5e7eb;
}
.btn-success {
background: #10b981;
color: white;
width: 100%;
margin-bottom: 1rem;
}
.btn-success:hover {
background: #059669;
}
.results-container {
padding: 1.5rem;
background: white;
border-radius: 0.5rem;
border: 1px solid #e5e7eb;
}
.verification-score {
margin-bottom: 1.5rem;
}
.score-header {
display: flex;
justify-content: space-between;
margin-bottom: 0.5rem;
font-size: 0.875rem;
}
.score-value {
font-size: 1.5rem;
font-weight: 700;
}
.score-bar {
width: 100%;
height: 0.75rem;
background: #e5e7eb;
border-radius: 9999px;
overflow: hidden;
}
.score-fill {
height: 100%;
transition: width 0.5s ease;
border-radius: 9999px;
}
.alert {
padding: 0.75rem;
border-radius: 0.375rem;
margin-bottom: 1rem;
font-size: 0.875rem;
}
.alert-success {
background: #d1fae5;
color: #065f46;
}
.alert-warning {
background: #fef3c7;
color: #92400e;
}
.comparisons {
display: flex;
flex-direction: column;
gap: 0.75rem;
}
.comparison-item {
border: 1px solid #e5e7eb;
border-radius: 0.5rem;
padding: 0.75rem;
}
.comparison-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 0.75rem;
}
.field-label {
font-weight: 500;
font-size: 0.875rem;
}
.status-badge {
padding: 0.25rem 0.5rem;
border-radius: 0.25rem;
font-size: 0.75rem;
font-weight: 500;
}
.comparison-values {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.user-value,
.google-suggestion {
padding: 0.5rem;
border-radius: 0.375rem;
}
.user-value {
background: #f9fafb;
}
.google-suggestion {
background: #eff6ff;
display: flex;
align-items: center;
gap: 0.5rem;
}
.value-label {
display: block;
font-size: 0.75rem;
color: #6b7280;
margin-bottom: 0.25rem;
}
.value-text {
font-weight: 500;
font-size: 0.875rem;
flex: 1;
}
.btn-accept {
padding: 0.25rem 0.75rem;
background: #3b82f6;
color: white;
border: none;
border-radius: 0.25rem;
cursor: pointer;
font-size: 0.75rem;
}
.btn-accept:hover {
background: #2563eb;
}
.empty-state {
text-align: center;
padding: 3rem;
color: #6b7280;
}
.modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.modal.hidden {
display: none;
}
.modal-content {
background: white;
padding: 2rem;
border-radius: 0.5rem;
max-width: 500px;
width: 90%;
}
.modal-content h3 {
margin-bottom: 0.5rem;
font-size: 1.25rem;
font-weight: 600;
}
.modal-description {
color: #6b7280;
font-size: 0.875rem;
margin-bottom: 1rem;
}
#verification-autocomplete {
margin-bottom: 1rem;
}
.modal-footer {
display: flex;
justify-content: flex-end;
}
</style>