Updated October 2026 by Marius: rewritten for Places API (New) and Google’s current Place Autocomplete widget, following Google’s documentation as of October 2026. First published in February 2023.
To add Google Maps address autocomplete to a form, enable Places API (New) and the Maps JavaScript API in Google Cloud, load the Maps JavaScript API on the page, and add Google’s Place Autocomplete widget (PlaceAutocompleteElement). When the visitor picks a suggestion, read the address components and copy the street, city, postcode and country into your form’s own fields. This guide does exactly that for a plain HTML form and for WordPress form plugins.
Older tutorials, including the first version of this one, attached new google.maps.places.Autocomplete(input) to an existing input. That class is part of Places API (Legacy), and as of October 2026 Places API (Legacy) can no longer be enabled for new Google Cloud projects. The Autocomplete class itself has been closed to new customers since March 1, 2025. So if you create a new project today and follow the old code, it will not work. If your site already uses the old code on an existing project, it keeps working: see the legacy section at the end.
Type an address in the demo field below to see Google’s suggestions. The demo runs the code from this tutorial on Places API (New): pick a suggestion and the Address field fills with the full address. Unlike the old widget, the new one draws its own input box, with a search icon and a clear button.
What you need
- A Google Cloud project with a billing account attached. Google requires billing even if you stay within the free monthly usage (costs below).
- A way to add HTML or JavaScript to the page with the form (a theme setting, a header/footer code plugin, or your page builder’s custom code box).
- The form itself, and the
nameattribute of each address field you want filled in.
Step 1: Enable Places API (New) and create an API key
- Sign in to the Google Cloud console and select (or create) the project for your site. Make sure a billing account is linked to it.
- Open APIs & Services > Library, search for Maps JavaScript API and click Enable.
- Back in the library, search for Places API (New) and enable it too. Pick the one with “(New)” in the name. The legacy one is listed as Places API (Legacy) and, on a new project, can no longer be enabled.
- Open Google Maps Platform > Credentials and create an API key.
Step 2: Restrict the API key
The key ends up in your page’s HTML, where anyone can read it. That is normal for the Maps JavaScript API and you cannot hide it. What stops other people from running up your bill with it is restrictions. Google’s security guidance recommends one application restriction plus API restrictions on every key:
- Application restriction: choose Websites and add each site that may use the key, with the scheme, for example
https://example.comandhttps://*.example.com(the wildcard coverswwwand other subdomains). Add your staging site separately if it needs the key. - API restrictions: choose Restrict key and select only Maps JavaScript API and Places API (New).
Avoid restricting to a full path such as https://example.com/checkout/. Google warns that most browsers strip the path from the referrer they send, so the key can fail.
Step 3: Load the Maps JavaScript API
Google’s current way to load the API is the bootstrap loader. Add this to the <head> of the page with the form and replace YOUR_API_KEY with your key. It replaces the old maps/api/js?key=...&libraries=places&callback=initAutocomplete script tag. The loader itself is small and downloads the rest of the API only when your code asks for a library, so there is no callback function to name.
<script>
(g=>{var h,a,k,p="The Google Maps JavaScript API",c="google",l="importLibrary",q="__ib__",m=document,b=window;b=b[c]||(b[c]={});var d=b.maps||(b.maps={}),r=new Set,e=new URLSearchParams,u=()=>h||(h=new Promise(async(f,n)=>{await (a=m.createElement("script"));e.set("libraries",[...r]+"");for(k in g)e.set(k.replace(/[A-Z]/g,t=>"_"+t[0].toLowerCase()),g[k]);e.set("callback",c+".maps."+q);a.src=`https://maps.${c}apis.com/maps/api/js?`+e;d[q]=f;a.onerror=()=>h=n(Error(p+" could not load."));a.nonce=m.querySelector("script[nonce]")?.nonce||"";m.head.append(a)}));d[l]?console.warn(p+" only loads once. Ignoring:",g):d[l]=(f,...n)=>r.add(f)&&u().then(()=>d[l](f,...n))})({
key: "YOUR_API_KEY",
v: "weekly",
});
</script>Code language: HTML, XML (xml)
If your page builder only accepts JavaScript, not HTML, paste the code between the <script> tags. Load the API only once per page.
Step 4: Make room for the search box in your form
The new widget is a self-contained element with its own input. Unlike the old Autocomplete class, it cannot be attached to an input you already have. So the search box goes above your address fields, and your own fields stay the ones that get submitted. The visitor can still correct them, and add an apartment number, after picking an address.
Add an empty element where the search box should appear. Here is a complete plain HTML form with one:
<form method="post" action="/save-address">
<div id="address-search"></div>
<label>Street address <input name="street" autocomplete="address-line1" required></label>
<label>Apartment, suite, unit <input name="street2" autocomplete="address-line2"></label>
<label>City <input name="city" autocomplete="address-level2" required></label>
<label>State / province <input name="state" autocomplete="address-level1"></label>
<label>Postcode <input name="postcode" autocomplete="postal-code" required></label>
<label>Country
<select name="country" autocomplete="country" required>
<option value="">Choose…</option>
<option value="US">United States</option>
<option value="CA">Canada</option>
</select>
</label>
<input type="hidden" name="full_address">
<button type="submit">Save address</button>
</form>Code language: HTML, XML (xml)
The autocomplete="..." attributes are standard HTML. They let the browser’s own address autofill keep working alongside Google’s.
On a plain HTML page you could write Google’s <gmp-place-autocomplete> tag directly in the markup, which is what Google’s address form sample does. The script below creates it inside an empty <div> instead, because form plugins often strip unknown tags from their HTML fields. Fluent Forms, for example, filters its Custom HTML field through WordPress’s wp_kses allowlist, which keeps a <div id="..."> but drops the Google tag.
Step 5: Add the autocomplete script
Add this script to the same page, after the loader from step 3 (in the footer is fine). Edit the settings block at the top to match your form; the rest can stay as it is.
const ADDRESS_AUTOCOMPLETE = {
// Empty element the search box is placed in.
container: '#address-search',
// Countries to search, as two-letter codes (up to 15). Use [] for worldwide.
countries: ['us'],
// CSS selectors for your form's fields. Use null for fields you don't have.
fields: {
street: '[name="street"]',
city: '[name="city"]',
state: '[name="state"]',
postcode: '[name="postcode"]',
country: '[name="country"]',
fullAddress: '[name="full_address"]',
},
// true: "1600 Amphitheatre Parkway". false: "Strada Lipscani 12".
numberFirst: true,
};
async function initAddressAutocomplete() {
const config = ADDRESS_AUTOCOMPLETE;
const container = document.querySelector(config.container);
if (!container) {
return; // No address form on this page.
}
if (!window.google?.maps?.importLibrary) {
console.error('Add the Google Maps bootstrap loader before this script.');
return;
}
const { PlaceAutocompleteElement } = await google.maps.importLibrary('places');
const form = container.closest('form') || document;
const search = new PlaceAutocompleteElement({
includedRegionCodes: config.countries,
includedPrimaryTypes: ['street_address'],
});
search.placeholder = 'Start typing your address';
container.append(search);
search.addEventListener('gmp-error', (event) => {
console.error('Place Autocomplete request failed', event);
});
search.addEventListener('gmp-select', async ({ placePrediction }) => {
const place = placePrediction.toPlace();
try {
await place.fetchFields({ fields: ['addressComponents', 'formattedAddress'] });
} catch (error) {
console.error('Could not fetch the address details', error);
return;
}
const part = (type, short = false) => {
const component = (place.addressComponents || []).find((c) => c.types.includes(type));
return (component && (short ? component.shortText : component.longText)) || '';
};
const number = part('street_number');
const route = part('route');
const street = config.numberFirst ? [number, route] : [route, number];
const values = {
street: street.filter(Boolean).join(' '),
city: part('locality') || part('postal_town') || part('sublocality_level_1'),
state: part('administrative_area_level_1', true),
postcode: part('postal_code'),
country: part('country', true),
fullAddress: place.formattedAddress || '',
};
for (const [key, value] of Object.entries(values)) {
const field = config.fields[key] && form.querySelector(config.fields[key]);
if (!field) {
continue;
}
field.value = value;
// Let form plugins notice the change (validation, conditional logic).
field.dispatchEvent(new Event('input', { bubbles: true }));
field.dispatchEvent(new Event('change', { bubbles: true }));
}
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', initAddressAutocomplete);
} else {
initAddressAutocomplete();
}Code language: JavaScript (javascript)
What it does:
google.maps.importLibrary('places')loads the Places library and gives youPlaceAutocompleteElement.includedRegionCodeslimits suggestions to the countries you list (up to 15 two-letter codes). It replaces the oldcomponentRestrictions: { country: 'us' }. Use[]for worldwide results.includedPrimaryTypes: ['street_address']limits suggestions to street addresses, so business names and cities don’t show up. Google also classifies some addresses aspremise(a building) orsubpremise(an apartment or unit). If addresses your visitors expect are missing, add those types to the list (up to five values from Table B).- When the visitor picks a suggestion, the widget fires a
gmp-selectevent.placePrediction.toPlace()turns it into aPlace, andfetchFields()downloads only the two fields the form needs:addressComponentsandformattedAddress. - Each address component has
types, alongText(“California”) and ashortText(“CA”). The script uses the long form for the city and postcode, and the short form for the state and country. The country code (“US”) matches the option values of most country dropdowns. - The city falls back to
postal_townand thensublocality_level_1. Google’s sample notes that the UK and Sweden usepostal_townfor the city, and that parts of New York City, such as Brooklyn, usesublocality_level_1. - Setting
numberFirst: falseputs the house number after the street name, as most of Europe writes it. - After filling each field, the script fires
inputandchangeevents, so form plugins re-run their validation and conditional logic.
If no suggestions appear, open the browser console. The widget fires a gmp-error event when Google rejects a request, for example because of a wrong key, and the script logs it. The Maps JavaScript API also prints its own error there when the key isn’t allowed on your site.
To style the search box, set CSS on gmp-place-autocomplete itself (border, border-radius, background-color, font) or on its parts, such as ::part(input). It defaults to color-scheme: light dark, so on a phone in dark mode it can turn dark on a light page. Add gmp-place-autocomplete { color-scheme: light; } if your form is light only.
Using it with WordPress form plugins
The approach above works with any form plugin that lets you add an HTML block to a form; in Fluent Forms that is the Custom HTML field. You need two things:
- An HTML or Custom HTML field above the address fields, containing only
<div id="address-search"></div>. - The real
nameattribute of each address input, for thefieldsselectors. The quickest way to find them is to right-click the field on the live form, choose Inspect, and read thenameattribute.
In Fluent Forms you can also see and change a field’s name in the field editor, under Advanced Options > Name Attribute. I built the demo form above with Fluent Forms.

If you use Fluent Forms’ Address field, its sub-fields share the Address field’s name attribute, which is address_1 by default. In the current version (6.2) they are named like this, so the settings become:
fields: {
street: '[name="address_1[address_line_1]"]',
city: '[name="address_1[city]"]',
state: '[name="address_1[state]"]',
postcode: '[name="address_1[zip]"]',
country: '[name="address_1[country]"]',
fullAddress: null,
},Code language: JavaScript (javascript)
The Fluent Forms country field is a dropdown whose option values are two-letter codes (US, RO), so the script’s country code selects the right country. If you renamed the Address field, replace address_1 with your name. Fluent Forms Pro also has built-in Google address autocomplete for its Address field. As of its July 2025 setup guide, it still asks you to enable “Places API”, so check that it works with Places API (New) before relying on it in a new Cloud project.
Disclosure: the Fluent Forms link is an affiliate link. If you buy through it, I earn a commission at no extra cost to you.
What it costs
Billing must be enabled, but a low-traffic form may stay within the free usage. As of October 2026, Google’s price list includes 10,000 free billable events per month for each of the two SKUs this form uses, Autocomplete Requests and Place Details Essentials. Past the free usage, the first price tier is $2.83 per 1,000 autocomplete requests and $5.00 per 1,000 Place Details Essentials requests.
The widget groups a visitor’s typing and their final pick into one session automatically. If the session ends with a Place Details request for Essentials fields only (addressComponents and formattedAddress both are), only the first 12 autocomplete requests in it are billed. If you also fetch fields from the higher tiers, such as opening hours or ratings, Google bills the closing request at its Enterprise + Atmosphere rate. So fetch only the fields you need. Prices change, so check the price list before you launch, and set a budget alert on the billing account.
If you’re still on the legacy Autocomplete class
If your site already runs the old google.maps.places.Autocomplete code on an existing project, you don’t have to rush. Google says existing Places API (Legacy) projects remain fully supported, and that it will give at least 12 months’ notice before support for the Autocomplete class is discontinued. It now only receives fixes for major regressions, though, and pages that use it show a deprecation warning in the browser console.
To migrate, enable Places API (New) on the same project, add it to the key’s API restrictions, and swap the code. Here is how the old pieces map to the new ones, from Google’s migration guide and API reference:
| Legacy | Places API (New) |
|---|---|
<script src="…/maps/api/js?key=…&libraries=places&callback=initAutocomplete"> | Bootstrap loader + await google.maps.importLibrary('places') |
new google.maps.places.Autocomplete(input, options) | new PlaceAutocompleteElement(options) or <gmp-place-autocomplete>. It brings its own input instead of using yours. |
componentRestrictions: { country: 'us' } | includedRegionCodes: ['us'] |
types: ['address'] | includedPrimaryTypes with up to five types, for example ['street_address'], or one of (regions) / (cities) |
bounds, strictBounds, bindTo('bounds', map) | locationBias (prefer an area) or locationRestriction (only that area) |
fields: ['address_components'] in the options | place.fetchFields({ fields: ['addressComponents'] }) after the visitor picks |
place_changed + autocomplete.getPlace() | gmp-select event + event.placePrediction.toPlace() |
address_components[].long_name / short_name, formatted_address | addressComponents[].longText / shortText, formattedAddress |
One thing you can drop. With the old class, a visitor could press Enter on text that wasn’t one of the suggestions, and your code got a place without geometry, which is why old code checks if (!place.geometry). The new widget only returns places picked from its suggestions, so that check isn’t needed.
Also watch out when copying code from tutorials about the new widget written before 2025. They may use gmp-placeselect, gmp-requesterror, componentRestrictions or types. Google replaced those in early 2025 with gmp-select, gmp-error, includedRegionCodes and includedPrimaryTypes.

Tip if you have problems with the browser autofilling in a bad address on the form. Passwords won’t be autofilled.
jQuery(document).ready(function(){
setTimeout(function() {
jQuery(‘#input_1_30’).attr(‘autocomplete’,’new-password’);
}, 2000);
});