Instant Canadian Address Autocomplete in Leaflet JS
Learn how to implement a blazing-fast, typo-tolerant address search bar on a Leaflet map using Geo Service's sovereign Canadian Photon endpoint.
⚡ What You Will Build
A pure Vanilla JavaScript search input that triggers instant address suggestions across all Canadian provinces and territories as the user types, flies the map camera to the selected coordinate, and drops a pin—without external bloated dependencies.
Step 1: HTML Setup & Leaflet Container
Include Leaflet's assets and create a simple container with an overlay search input:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Canadian Address Autocomplete</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<style>
#map { height: 500px; width: 100%; border-radius: 12px; }
.search-box { position: absolute; top: 15px; left: 60px; z-index: 1000; width: 320px; }
.search-input { width: 100%; padding: 10px 14px; font-size: 14px; border-radius: 8px; border: 1px solid #ccc; box-shadow: 0 2px 6px rgba(0,0,0,0.15); }
.results-list { background: white; list-style: none; padding: 0; margin: 4px 0 0; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); max-height: 220px; overflow-y: auto; }
.results-list li { padding: 8px 12px; cursor: pointer; border-bottom: 1px solid #eee; font-size: 13px; }
.results-list li:hover { background: #f0f4ff; }
</style>
</head>
<body>
<div style="position: relative;">
<div class="search-box">
<input type="text" id="address-input" class="search-input" placeholder="Search Canadian address..." autocomplete="off">
<ul id="results" class="results-list" style="display: none;"></ul>
</div>
<div id="map"></div>
</div>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script src="app.js"></script>
</body>
</html>
Step 2: Connect Autocomplete to the Photon API
In your app.js, initialize the map and fetch address suggestions with debounce. Notice how we pass your domain-scoped Browser Key in the X-Api-Key header:
// 1. Initialize map centered on Toronto / Southern Ontario
const map = L.map('map').setView([43.6532, -79.3832], 12);
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '© OpenStreetMap contributors'
}).addTo(map);
let activeMarker = null;
const input = document.getElementById('address-input');
const resultsList = document.getElementById('results');
// 2. Debounced search listener
let debounceTimeout = null;
input.addEventListener('input', (e) => {
clearTimeout(debounceTimeout);
const query = e.target.value.trim();
if (query.length < 3) {
resultsList.style.display = 'none';
return;
}
debounceTimeout = setTimeout(async () => {
try {
// Photon Canadian geocoding endpoint with your browser key
const url = `https://geo.jerielbenavides.com/api?q=${encodeURIComponent(query)}&limit=5&bbox=-141.0,41.6,-52.6,83.1`;
const res = await fetch(url, {
headers: { 'X-Api-Key': 'YOUR_BROWSER_KEY_HERE' }
});
const data = await res.json();
renderResults(data.features || []);
} catch (err) {
console.error('Geocoding error:', err);
}
}, 250);
});
// 3. Render suggestions and handle pin placement
function renderResults(features) {
resultsList.innerHTML = '';
if (!features.length) {
resultsList.style.display = 'none';
return;
}
features.forEach(feature => {
const p = feature.properties;
const [lon, lat] = feature.geometry.coordinates;
const label = [p.name, p.street, p.city, p.state, p.postcode].filter(Boolean).join(', ');
const li = document.createElement('li');
li.textContent = label;
li.addEventListener('click', () => {
input.value = label;
resultsList.style.display = 'none';
map.flyTo([lat, lon], 16);
if (activeMarker) map.removeLayer(activeMarker);
activeMarker = L.marker([lat, lon]).addTo(map).bindPopup(label).openPopup();
});
resultsList.appendChild(li);
});
resultsList.style.display = 'block';
}
🔒 Security Best Practice: Use Browser Keys
Never expose your secret Server Key in client-side JavaScript. Instead, navigate to your Dashboard → API Keys and create a Browser Key. Browser keys are restricted strictly to your allowed web domains (e.g. https://myapp.ca) and can only perform address lookups.
Ready to build your Canadian map experience?
Get your free API key in seconds and start searching Canadian addresses with zero query logging.