Interactive maps are a staple of modern web development, but integrating them into WordPress often leads down a messy path. Between the rising costs of proprietary APIs like Google Maps, the clutter of inline <script> tags, and themes tightly coupled to backend data, map implementations can quickly turn into a technical debt nightmare.
It was time for a clean slate. I recently went back into one of my older repositories to completely refactor how I approach mapping in WordPress. The goal? A robust, decoupled architecture using 100% open-source tools that is just as comfortable in a traditional theme as it is in a headless Jamstack environment.
Here is how I rebuilt my mapping architecture using Leaflet.js, OpenStreetMap, and Secure Custom Fields (SCF).
Ditching Paid APIs for Open Source
The first step was removing reliance on expensive, proprietary map providers. Leaflet.js is a brilliantly lightweight, open-source JavaScript library for interactive maps. By pairing it with tile layers from OpenStreetMap, you get a completely free, highly customizable mapping solution that doesn’t require API keys, credit cards, or strict rate limits.
The Shift to Secure Custom Fields (SCF)
With the recent shifts in the WordPress ecosystem, I transitioned the backend data structure from Advanced Custom Fields (ACF) to Secure Custom Fields (SCF).
Because SCF is a direct, community-maintained fork, it acts as a perfect drop-in replacement. In this architecture, we use it to create a dynamic Repeater field, allowing content editors to easily add multiple latitudes, longitudes, and popup descriptions to a single “Event” Custom Post Type (CPT).
True Separation of Concerns: Plugin vs. Theme
The biggest flaw in older map tutorials is mixing data creation with data presentation. If a user switches themes, they shouldn’t lose their map data!
To solve this, I split the architecture into two distinct, modular layers:
- The Core Plugin (Backend): A lightweight plugin handles the database layer. It registers the Event CPT and the SCF Repeater fields entirely via PHP. To make it a true plug-and-play resource, I added an automated script: the moment you activate the plugin, it generates dummy events across Europe (from Patras and Athens to Berlin and London) so you can test the map immediately.
- The Theme Presentation (Frontend): The frontend implementation lives safely in the child theme. It features a clean HTML wrapper (
<div id="scf-leaflet-map"></div>) and intelligently enqueues the JavaScript logic only when an Event post or archive is being viewed.
Passing Data the Clean Way: wp_localize_script
Writing PHP variables directly into inline JavaScript is a security and performance anti-pattern. Instead, the backend securely passes the SCF location data to the frontend JavaScript engine using wp_localize_script.
The frontend JS simply listens for this structured object and plots the markers. I also upgraded the logic to include Auto-Framing. Whether you are looking at a single event with one marker, or a massive master archive map with dozens of locations, Leaflet’s featureGroup automatically calculates the boundaries and zooms to fit perfectly.
document.addEventListener("DOMContentLoaded", function () {
if (!document.getElementById("scf-leaflet-map") || typeof scfMapData === "undefined") return;
const map = L.map('scf-leaflet-map');
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);
const markers = [];
scfMapData.locations.forEach(location => {
const marker = L.marker([location.latitude, location.longitude]).addTo(map);
if (location.popup_text) marker.bindPopup(location.popup_text);
markers.push(marker);
});
// Auto-fit the map bounds to show all markers dynamically
if (markers.length > 0) {
const group = new L.featureGroup(markers);
map.fitBounds(group.getBounds(), { padding: [30, 30] });
if (markers.length === 1) map.setZoom(13);
}
});
The Evolution of the Codebase: Old vs. New
Refactoring is all about looking at your past work and finding a cleaner path forward. If you look at an older iteration of my Leaflet implementation, the technical debt is immediately visible. The old approach was rigid: it often relied on hardcoded coordinates, manual map-centering calculations, and the dangerous anti-pattern of echoing PHP variables directly into inline <script> tags within the HTML. It worked, but it was tightly coupled and fragile.
The older iteration gist
Compare that to the modernized implementation. The evolution of the architecture is night and day. By utilizing wp_localize_script, the PHP backend now securely passes a sanitized JSON object (scfMapData) to the frontend. The new JavaScript is entirely decoupled from the WordPress core. Furthermore, by introducing Leaflet’s featureGroup logic, the script intelligently calculates the boundaries of all active markers and auto-frames the map perfectly, eliminating the need to ever manually set zoom levels or center coordinates again. It is cleaner, safer, and entirely dynamic.
The modernized implementation gist
The real power of this modernization lies in
wp_localize_script. In older implementations, developers often found themselves dynamically echoing PHP arrays straight into inline<script>tags within their HTML templates. It is a maintenance nightmare. By usingwp_localize_script, WordPress handles the JSON encoding safely and securely behind the scenes. Our backend queries the database, formats the array, and hands a cleanscfMapDataobject directly to the decoupled JavaScript file. The frontend never touches the server, and the backend never writes inline UI logic.
Ready for the Jamstack
Because I often build in headless environments using Gatsby and Netlify, I couldn’t just build a classic PHP theme implementation and call it a day.
The core plugin also registers a custom REST API endpoint (/wp-json/custom/v1/event-maps). This endpoint intentionally bypasses the heavy, standard WordPress REST responses. Instead, it delivers a clean, lightning-fast JSON payload containing only the map coordinates and popup HTML, ready to be consumed by any decoupled React, Vue, or Astro application.
Explore the Code
Building clean, decoupled architecture is an ongoing journey of refactoring and refining. If you want to see the full implementation, explore the code, or drop it into your own local environment, check out the freshly launched repository on GitHub: Explore the wordpress-scf-leaflet repository on GitHub.
Test Drive the Demo
Feel free to explore the site to see the code in action. Check out a Single Event page to see how individual location pins are plotted, or navigate to the Event Archive to see the JavaScript auto-framing feature seamlessly zoom and bound a master map containing every single location across Europe.

Leave a Reply