Multiple Pins Map
A WordPress plugin that displays an interactive map with multiple location pins, powered by Leaflet and OpenStreetMap
by Multiple Pins Map · github.com/robbdeveloper/multiple-pins-map
Install
The author publishes release zips, so WP-CLI can install straight from GitHub:
wp plugin install https://github.com/robbdeveloper/multiple-pins-map/releases/download/v1.0.0/multiple-pins-map.zipA WordPress plugin that displays an interactive map with multiple location pins, powered by Leaflet and OpenStreetMap. Locations are pulled from your posts via Advanced Custom Fields (ACF).
Features
- Shortcode-based — drop
[multiple_pins_map]into any page or post - Responsive layout — desktop shows a synced list + map panel; mobile uses an accordion with per-location mini-maps (breakpoint: 980px)
- ACF-powered — reads latitude, longitude, address, directions URL, and custom pin icons from ACF fields
- Customizable — override post type, field names, map tiles, zoom, colors, fonts, and heights via shortcode attributes
- Custom SVG pins — per-location or default pin icons via URL
- Accessible — ARIA labels on list and map panels, keyboard-friendly list rows
- Translated — includes Italian, French, German, and Spanish translations
Requirements
| Requirement | Version |
|---|---|
| WordPress | 5.8+ |
| PHP | 7.4+ |
| Advanced Custom Fields | Any recent version |
The plugin also loads Leaflet, map tiles, and Google Fonts from external CDNs, so your site needs internet access on pages where the shortcode is used.
Installation
From a GitHub Release (recommended)
- Go to the Releases page
- Download
multiple-pins-map.zipfrom the latest release - In WordPress admin, go to Plugins → Add New → Upload Plugin
- Upload the zip file and click Install Now, then Activate
Manual install
- Clone or download this repository
- Copy the plugin folder into
wp-content/plugins/multiple-pins-map/ - Activate Multiple Pins Map under Plugins
Setup
1. Create your post type
By default the shortcode queries the structure post type. You can use any registered post type — just pass it via the post_type attribute.
2. Add ACF fields
Create the following ACF fields on your post type (field names are configurable via shortcode attributes):
| Field name (default) | Type | Description |
|---|---|---|
address |
Text | Street address shown in the list and popups |
latitude |
Number / Text | Latitude coordinate |
longitude |
Number / Text | Longitude coordinate |
map_url |
URL | External link for the "Directions" button |
pin_icon |
Textarea | Inline SVG markup for a custom pin icon (optional) |
Coordinate formats supported:
- Separate
latitude/longitudenumber or text fields - ACF Google Map field arrays — the plugin reads
lat/lngorlatitude/longitudesub-keys automatically
Posts without valid coordinates are skipped silently.
3. Publish locations
Create and publish posts with coordinates filled in. Only publish status posts are included.
4. Add the shortcode
[multiple_pins_map]
Place it in a page, post, or block that supports shortcodes.
Usage
Basic example
[multiple_pins_map]
This renders all published structure posts with the default ACF field names, sorted by menu_order ascending.
Custom post type and fields
[multiple_pins_map
post_type="store"
field_address="store_address"
field_lat="store_lat"
field_lng="store_lng"
field_url="directions_link"
]
Styled example
[multiple_pins_map
accent="#1a73e8"
radius="8px"
map_height="600px"
map_height_mobile="400px"
zoom="14"
default_pin_icon="https://example.com/pin.svg"
]
Shortcode attributes
Query
| Attribute | Default | Description |
|---|---|---|
post_type |
structure |
WordPress post type to query |
posts_per_page |
-1 |
Number of posts (-1 = all) |
orderby |
menu_order |
WP_Query orderby parameter |
order |
ASC |
Sort order: ASC or DESC |
ACF field mapping
| Attribute | Default | Description |
|---|---|---|
field_address |
address |
ACF field for the location address |
field_lat |
latitude |
ACF field for latitude |
field_lng |
longitude |
ACF field for longitude |
field_url |
map_url |
ACF field for the directions/external URL |
field_icon |
pin_icon |
ACF field for per-location SVG pin markup |
Map behavior
| Attribute | Default | Description |
|---|---|---|
zoom |
15 |
Map zoom level (1–19) |
fit_bounds |
true |
Auto-fit map to show all pins (when more than one) |
tiles |
OpenStreetMap URL | Leaflet tile layer URL template |
tile_attribution |
OSM attribution HTML | Attribution string for the tile layer |
tile_subdomains |
abc |
Subdomains for tile servers (e.g. {s} placeholder) |
default_pin_icon |
(empty) | URL to an SVG file used as the default pin icon |
Appearance
| Attribute | Default | Description |
|---|---|---|
accent |
(theme default) | Accent color (CSS hex, e.g. #1a73e8) |
radius |
(theme default) | Border radius for list rows (e.g. 8px) |
map_height |
540px |
Map height on desktop |
map_height_mobile |
320px |
Map height on mobile accordion |
font_heading |
(theme default) | CSS font-family for headings |
font_body |
(theme default) | CSS font-family for body text |
load_fonts |
true |
Load bundled Google Fonts (Epilogue + Fraunces) |
Customization
CSS custom properties
The plugin uses CSS variables on the .mpm wrapper. You can override them via shortcode attributes (accent, radius, map_height, etc.) or in your theme's CSS:
.mpm {
--mpm-accent: #1a73e8;
--mpm-radius: 8px;
--mpm-map-height: 600px;
--mpm-map-height-mobile: 400px;
--mpm-font-heading: 'Georgia', serif;
--mpm-font-body: 'Arial', sans-serif;
}
Available variables include --mpm-accent, --mpm-active, --mpm-text, --mpm-muted, --mpm-bg, --mpm-radius, --mpm-map-height, --mpm-map-height-mobile, --mpm-font-heading, --mpm-font-body, and more (see assets/css/mpm.css).
Custom tile providers
Pass a different tile URL and attribution:
[multiple_pins_map
tiles="https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png"
tile_attribution="© OpenStreetMap © CARTO"
tile_subdomains="abcd"
]
Custom pin icons
- Per location: store SVG markup in the ACF
pin_iconfield - Global default: pass a URL to an SVG file via
default_pin_icon
SVG content is sanitized server-side for security.
Translations
The plugin text domain is multiple-pins-map. Bundled translations:
| Locale | Files |
|---|---|
| Italian | languages/multiple-pins-map-it_IT.{po,mo} |
| French | languages/multiple-pins-map-fr_FR.{po,mo} |
| German | languages/multiple-pins-map-de_DE.{po,mo} |
| Spanish | languages/multiple-pins-map-es_ES.{po,mo} |
To add a new language, copy languages/multiple-pins-map.pot, translate it, compile to .mo, and place both files in languages/.
Releasing a new version
Releases are automated via GitHub Actions. To publish a new version:
- Bump the version in two places inside
multiple-pins-map.php:- Plugin header:
Version: X.Y.Z - Constant:
define( 'MPM_VERSION', 'X.Y.Z' );
- Plugin header:
- Commit and push to the
mainbranch
The Release workflow will:
- Read the version from the plugin header
- Verify it matches
MPM_VERSION(fails if they differ) - Skip if tag
vX.Y.Zalready exists - Build
multiple-pins-map.zip(WordPress-ready, with a top-levelmultiple-pins-map/folder) - Create a GitHub Release tagged
vX.Y.Zwith the zip attached
Pushes to main that don't change the version are ignored — no duplicate releases.
What gets included in the zip
multiple-pins-map.phpassets/(CSS and JS)languages/(.moand.potfiles;.posource files are excluded)
Excluded: .git, .github, .cursor, build/, .DS_Store
Development
Project structure
multiple-pins-map/
├── multiple-pins-map.php # Main plugin file (shortcode, queries, asset loading)
├── assets/
│ ├── css/mpm.css # Styles and CSS custom properties
│ └── js/mpm.js # Leaflet map logic (desktop + mobile)
├── languages/ # Translation files
└── .github/workflows/
└── release.yml # Automated release on version bump
Local testing
- Symlink or copy the plugin folder into your local WordPress
wp-content/plugins/directory - Activate the plugin
- Ensure ACF is installed and your test posts have coordinates
- Add
[multiple_pins_map]to a test page
License
This plugin is provided as-is. See the repository for license details.
Releases
1 release. Each count is every asset in that release; expand a row for the breakdown.