Create Google Map with Multiple Dynamic Markers Using Advanced Custom Fields

Create Google Map with Multiple Dynamic Markers Using Advanced Custom Fields

If you follow my site, you must know that I am a big fan of the Advanced Custom Fields plugin. It is super useful and packed with great features. Previously, I showed how to create and edit posts from the front-end with Advanced custom fields plugin, which has been very useful for many of this website visitors. In this post, I am going to explain how to create a Google Map with multiple dynamic markers using the Advanced Custom Fields plugin for WordPress.

Please Note

You will need a valid Google Maps API key with billing activated to display the map correctly.

The Scenario

Suppose you are running a business that has multiple outlets throughout the country. You want to showcase all the locations so users can see where they are. Meanwhile, you have some special outlets which have greater inventory so you want to highlight them. There are some other stores that are upcoming and not open to public yet.

So, you end up with 3 (three) type of outlets

  1. Regular Outlets
  2. Special Outlets
  3. Upcoming Outlets

Our plan is to show these three type of outlets with different markers on a map so they appear nice and easy to differentiate.

Initial Setup

For the outlets, we are going to create a custom post type called “Locations”. This is easy to create with a plugin called “Custom Post Types UI”. Or you can add the following code to functions.php file.

<?php /** * Post Type: Locations */ function cpt_location() { $labels = array( "name" => __( "Locations", "text_domain" ), "singular_name" => __( "Location", "text_domain" ), ); $args = array( "label" => __( "Locations", "text_domain" ), "labels" => $labels, "description" => "", "public" => true, "publicly_queryable" => true, "show_ui" => true, "delete_with_user" => false, "show_in_rest" => true, "rest_base" => "", "rest_controller_class" => "WP_REST_Posts_Controller", "has_archive" => false, "show_in_menu" => true, "show_in_nav_menus" => true, "exclude_from_search" => false, "capability_type" => "post", "map_meta_cap" => true, "hierarchical" => false, "rewrite" => array( "slug" => "location", "with_front" => true ), "query_var" => true, "supports" => array( "title" ), ); register_post_type( "location", $args ); } add_action( 'init', 'cpt_location' );
Code language: HTML, XML (xml)

Once we have created the custom post type, you will see a new “Locations” link in your Dashboard.

Now we are going to create a field group with the Advanced Custom Fields (ACF) plugin. I created the fields like the following image. If you need the exact copy, you can download here.

Now, if you head over to add a new location. You will see something like the following image.

The error message you see is showing because we did not add a Google Maps API yet. Due to recent changes by Google, we require an account with activated billing information to be able to get a Google Maps API key. Without the billing, you will not be able to use it properly.

Getting Google Maps API

  • Head over to
  • Click “Get Started”
  • Check Maps & Places then click “Continue”
  • Select or Create a Project
  • Enable billing for the project you selected or created. You will be asked to “Create Billing Account” if you don’t already have billing information added.
  • Once your billing is all set, go to API & Services > Credentials
  • Click “Create Credentials” > API key and copy the API key.
  • You may set “HTTP referrers” restriction. (optional)
  • API Library and enable the following API
    • Maps JavaScript API
    • Geocoding API
    • Places API
  • You will need to make sure there is no quota limitation in these API’s. So, go to each of their pages (Maps, Geocoding, Places) and then to the “Quotas” tab and check if all limits are appropriate.
  • Now your Google Maps API key is ready to be used.

Setting Google Maps API

Now that we have the Google Maps API key, we need to add to our website. You can simply add the following code to functions.php file to set the Google Maps API key. Don’t forget to add the API key in the mentioned position as indicated!

function my_acf_init() { acf_update_setting('google_api_key', 'YOUR_GOOGLE_MAPS_API_HERE'); } add_action('acf/init', 'my_acf_init');
Code language: JavaScript (javascript)

With this added, our map should work fine in the back end. So, we will go ahead and add the location posts we need.

Enqueue Scripts

Now we need to enqueue two scripts. First the Google Maps JavaScript API and then a custom script that we can find at Advanced Custom Fields documentation page for Google Maps Field.

First, create a JavaScript file named gmaps.js in your child-theme folder. If you are going to do this without a child theme (not recommended), you will need to replace get_stylesheet_directory_uri() with get_template_directory_uri() in the following code.

Add the following code to your functions.php file. Replace “YOUR_GOOGLE_MAPS_API_HERE” portion with your API key.

function google_maps_scripts() { if (!is_admin()) { wp_register_script('googlemapsapi', '', array(), '', false); wp_enqueue_script('googlemapsapi'); wp_register_script('gmaps-init', get_stylesheet_directory_uri().'/gmaps.js', array(), '', false); wp_enqueue_script('gmaps-init'); } } add_action('wp_enqueue_scripts', 'google_maps_scripts', 100);
Code language: PHP (php)

We are loading these scripts only in the front end as we don’t need them in the backend. Hense, using the if statement with is_admin() condition

Now we are going to add the following code to the gmaps.js file we created. This portion of code is slightly modified from the exisiting code provided in Advanced Custom Fields documentation here.

(function($) { /* * new_map * * This function will render a Google Map onto the selected jQuery element * * @type function * @date 8/11/2013 * @since 4.3.0 * * @param $el (jQuery element) * @return n/a */ function new_map( $el ) { // var var $markers = $el.find('.marker'); // vars var args = { zoom : 16, center : new google.maps.LatLng(0, 0), mapTypeId : google.maps.MapTypeId.ROADMAP }; // create map var map = new google.maps.Map( $el[0], args); // add a markers reference map.markers = []; // add markers $markers.each(function(){ add_marker( $(this), map ); }); // center map center_map( map ); // return return map; } /* * add_marker * * This function will add a marker to the selected Google Map * * @type function * @date 8/11/2013 * @since 4.3.0 * * @param $marker (jQuery element) * @param map (Google Map object) * @return n/a */ function add_marker( $marker, map ) { // var var latlng = new google.maps.LatLng( $marker.attr('data-lat'), $marker.attr('data-lng') ); var icon = $marker.attr('data-img'); // create marker var marker = new google.maps.Marker({ position : latlng, map : map, icon : icon }); // add to array map.markers.push( marker ); // if marker contains HTML, add it to an infoWindow if( $marker.html() ) { // create info window var infowindow = new google.maps.InfoWindow({ content : $marker.html() }); // show info window when marker is clicked google.maps.event.addListener(marker, 'click', function() { map, marker ); }); } } /* * center_map * * This function will center the map, showing all markers attached to this map * * @type function * @date 8/11/2013 * @since 4.3.0 * * @param map (Google Map object) * @return n/a */ function center_map( map ) { // vars var bounds = new google.maps.LatLngBounds(); // loop through all markers and create bounds $.each( map.markers, function( i, marker ){ var latlng = new google.maps.LatLng(, marker.position.lng() ); bounds.extend( latlng ); }); // only 1 marker? if( map.markers.length == 1 ) { // set center of map map.setCenter( bounds.getCenter() ); map.setZoom( 16 ); } else { // fit to bounds map.fitBounds( bounds ); } } /* * document ready * * This function will render each map when the document is ready (page has loaded) * * @type function * @date 8/11/2013 * @since 5.0.0 * * @param n/a * @return n/a */ // global var var map = null; $(document).ready(function(){ $('.acf-map').each(function(){ // create map map = new_map( $(this) ); }); //zoom google.maps.event.addListener( map, 'zoom_changed', function( e ) { var zoom = map.getZoom(); if(zoom!= 5) { var bounds = map.getBounds(); myLatLngss = []; $.each( map.markers, function( i, marker ){ var myLatLng = new google.maps.LatLng(, marker.position.lng() ); if(bounds.contains(myLatLng)===true) { myLatLngss.push( myLatLng ); } else { } }); if(myLatLngss.length > 0) { document.cookie = "coordn="+myLatLngss; $("#customzm").load(location.href + " #customzm"); } } }); google.maps.event.addListener(map, 'dragend', function() { //alert('map dragged'); var bounds = map.getBounds(); myLatLngss = []; $.each( map.markers, function( i, marker ){ var myLatLng = new google.maps.LatLng(, marker.position.lng() ); if(bounds.contains(myLatLng)===true) { myLatLngss.push( myLatLng ); } else { } if(myLatLngss.length > 0) { document.cookie = "coordn="+myLatLngss; $("#customzm").load(location.href + " #customzm"); } }); } ); }); })(jQuery);
Code language: JavaScript (javascript)

Create Shortcode

We are now going to create a shortcode to show the map. This will be very useful as we will be able to use it almost anywhere. So, add the following code to your functions.php file.

// Locations Map Shortcode - [locations_map] function locations_map (){ $args = array( 'post_type' => 'location', 'posts_per_page' => -1, ); $locations_query = new WP_QUERY($args); if ( $locations_query->have_posts() ) { ob_start(); ?> <div class="acf-map" style="overflow: hidden; position: relative;"> <?php while ( $locations_query->have_posts() ) { $locations_query->the_post(); $address = get_field('address'); $title = get_the_title(); $outlet_type = get_field('outlet_type'); $phone = get_field('phone'); if ($outlet_type == 'regular') { $type_icon = get_stylesheet_directory_uri().'/images/green-marker.png'; $outlet_text = 'Regular Outlet'; } elseif ($outlet_type == 'special') { $type_icon = get_stylesheet_directory_uri().'/images/red-marker.png'; $outlet_text = 'Special Outlet'; } else { $type_icon = get_stylesheet_directory_uri().'/images/gray-marker.png'; $outlet_text = 'Upcoming Outlet'; } ?> <div class="marker" data-lat="<?php echo $address['lat']; ?>" data-lng="<?php echo $address['lng']; ?>" data-img="<?php echo $type_icon; ?>"> <div class="inside-marker"> <h5><?php echo $title; ?></h5> <?php echo $outlet_text.'<br>'; if($phone) { echo 'Phone: '.$phone; } ?> </div> </div> <?php } ?> </div> <?php wp_reset_postdata(); } return ob_get_clean(); } add_shortcode( 'locations_map', 'locations_map' );
Code language: JavaScript (javascript)

In this code portion, we doing a query to get all the posts from location custom post type. Then we are adding data from the posts inside the loop. The div acf-map is going to wrap the map with all the markers.

The variable $type_icon holds info about the marker image that is used in gmaps.js file. This part helps us display dynamic markers based on values.

We will need to upload 3 image files called red-marker.png, green-marker.png and gray-marker.png. These will need to be uploaded to the images folder in our child theme as indicated in our code. For non-child theme users, you will need to replace get_stylesheet_directory_uri() with get_template_directory_uri() as done previously.

CSS and Animation

We need to add the following CSS code in our theme (or plugin if you use any). Most themes allow adding Custom CSS in Appearance > Customize > Additional CSS. You may tweak the code to your preference.

.acf-map { width: 100%; height: 700px; border: #ccc solid 1px; margin: 20px 0; }
Code language: CSS (css)

Finally, use the shortcode [locations_map] to a page or post and check your beautiful map!

The Output

Dynamic Markers in Google Maps with Advanced Custom Fields


This is a fairly complex project and it is very normal to get into errors. The most issues will be from Google Maps API. Google Cloud Console gets changed every now and then and you might see the steps are different but they should be very similar.

Watermarked “for development purposes only” error will most likely be shown if:

  • You did not use the API correctly
  • Your account is not billing enabled
  • A self-imposed quota/limit is set in your API
  • Your billing is no longer valid
  • Conflicting plugins/themes
  • JavaScript errors

Hopefully you will not run into any of these. Good Luck!

Last Updated: June 7, 2020

Al-Mamun Talukder

About Al-Mamun Talukder

WordPress Developer. Minimalist Designer. Tech Enthusiast. Music & Movie Freak. Loves Cars. Founder of WolfDevs.

Connect with me: Upwork, GitHub, Facebook, Twitter