A flexible, accessible, and secure Svelte component leveraging the Google Maps Places Autocomplete API (New). Winner of the Google Maps Platform Awards 2025, recognising excellence in Google Maps Platform development.
The component handles API loading, session tokens, debounced fetching, and accessibility, allowing you to focus on building your application. It intelligently manages the Google Maps API loader, creating a shared instance via Svelte's context that prevents conflicts with other map components on the same page.
Two initialisation patterns:
options.classes propclear(), focus(), getRequestParams(), setRequestParams(), setFetchFields(), and getFetchFields() methodsonResponse and onError callbacks for complete controlExplore live examples showcasing different features and use cases:
π Basic Example - Get started with the simplest implementation
π Reactive Parameters - Dynamically change search criteria based on user input, such as filtering by country or switching languages
βοΈ Custom Request Parameters - Configure advanced search options including language, region, location bias, and place types
π Retain Input Value - Keep the selected address visible in the input field after selection
This component has been recognised as a winner of the Google Maps Platform Awards 2025 by the Google Developer Program. This award celebrates outstanding projects that demonstrate exceptional use of Google Maps Platform APIs, innovation, and contribution to the developer community.
Learn more about the Google Maps Platform Awards
npm install places-autocomplete-svelte
# or
yarn add places-autocomplete-svelte
For simple use cases, just pass your API key to the component. It will automatically handle the Google Maps loader initialisation:
<script lang="ts">
import { PlaceAutocomplete } from 'places-autocomplete-svelte';
import type { PlaceResult } from 'places-autocomplete-svelte/interfaces';
// Get API Key securely (e.g., from environment variables)
const PUBLIC_GOOGLE_MAPS_API_KEY = import.meta.env.VITE_PUBLIC_GOOGLE_MAPS_API_KEY;
const handleResponse = (response: PlaceResult) => {
console.log('Selected:', response.formattedAddress);
};
const handleError = (error: string) => {
console.error('Error:', error);
};
</script>
<PlaceAutocomplete
{PUBLIC_GOOGLE_MAPS_API_KEY}
onResponse={handleResponse}
onError={handleError}
/>
For applications using multiple Google Maps libraries (e.g., places, maps, marker) or multiple map components, initialise the loader once in a parent component.
Benefits:
When to use manual initialisation:
maps, marker, geometry, etc.)// In +layout.svelte or +page.svelte
<script lang="ts">
import { browser } from '$app/environment';
import { PlaceAutocomplete } from 'places-autocomplete-svelte';
import { setGMapsContext, initialiseGMaps, importLibrary } from 'places-autocomplete-svelte/gmaps';
import { onMount } from 'svelte';
import type { PlaceResult } from 'places-autocomplete-svelte/interfaces';
// 1. Set the context at the top level (must be synchronous)
setGMapsContext();
// 2. Initialise the loader in the browser
if (browser) {
initialiseGMaps({
key: import.meta.env.VITE_PUBLIC_GOOGLE_MAPS_API_KEY,
v: 'weekly'
}).catch((error) => {
console.error('Failed to initialise Google Maps:', error);
});
}
// 3. Load additional libraries as needed
let map: google.maps.Map;
onMount(async () => {
const { Map } = await importLibrary('maps');
const mapElement = document.getElementById('map');
if (mapElement) {
map = new Map(mapElement, {
center: { lat: 51.5072, lng: -0.1276 },
zoom: 10,
mapId: 'YOUR_MAP_ID'
});
}
});
// 4. Handle autocomplete responses
const handleResponse = (response: PlaceResult) => {
console.log('Selected:', response.formattedAddress);
if (response.location && map) {
map.setCenter(response.location);
map.setZoom(15);
}
};
const handleError = (error: string) => {
console.error('Error:', error);
};
</script>
<!-- The component automatically uses the shared context -->
<!-- No need to pass PUBLIC_GOOGLE_MAPS_API_KEY when using manual initialisation -->
<PlaceAutocomplete
onResponse={handleResponse}
onError={handleError}
/>
<div id="map" class="h-96 w-full"></div>
Available helper functions from places-autocomplete-svelte/gmaps:
setGMapsContext() - Creates the shared context (call once at the top level)getGMapsContext() - Retrieves the context (returns stores for initialisation state and errors)hasGMapsContext() - Checks if context exists (useful for conditional logic)initialiseGMaps(options) - Initialises the loader with your API key and optionsinitialiseGMapsNoContext(options) - Initialises without context (for edge cases)importLibrary(library) - Dynamically imports Google Maps librariesYour Google Maps API Key is a sensitive credential. To prevent unauthorised use and unexpected charges, you must restrict it.
your-domain.com/*).This component is designed to be secure out-of-the-box. It safely renders user-input and API responses to prevent Cross-Site Scripting (XSS) vulnerabilities.
This component is built to be accessible and follows the WAI-ARIA Authoring Practices for a Combobox.
ArrowUp, ArrowDown, select with Enter, and close the list with Escape.role="combobox", aria-autocomplete, aria-expanded, and aria-activedescendant to provide a clear experience for screen reader users.| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
PUBLIC_GOOGLE_MAPS_API_KEY |
string |
No* | - | Your Google Maps API Key. Required for automatic initialisation. Optional if you've initialised the loader in a parent component using initialiseGMaps(). |
onResponse |
(response: PlaceResult) => void |
Yes | - | Callback triggered when a user selects a place. Receives the full place details object. |
onError |
(error: string) => void |
Yes | - | Callback triggered when an error occurs (API loading, network issues, etc.). |
fetchFields |
string[] |
No | ['formattedAddress', 'addressComponents'] |
Place Data Fields to request from the API. See Place Data Fields. Affects API billing. |
requestParams |
Partial<RequestParams> |
No | { inputOffset: 3 } |
Parameters for the Autocomplete API request (language, region, location bias, etc.). See RequestParams interface. |
options |
Partial<ComponentOptions> |
No | { debounce: 100 } |
UI and behavior options (placeholder, debounce delay, distance display, custom classes, etc.). See ComponentOptions interface. |
*Either PUBLIC_GOOGLE_MAPS_API_KEY prop OR manual initialisation with initialiseGMaps() is required.
Get a reference to the component instance using bind:this to call its methods directly.
Example:
<script lang="ts">
import PlaceAutocomplete from 'places-autocomplete-svelte';
let autocompleteComponent: PlaceAutocomplete | undefined = $state(undefined);
</script>
<PlaceAutocomplete bind:this={autocompleteComponent} ... />
<button onclick={() => autocompleteComponent?.clear()}>Clear</button>
<button onclick={() => autocompleteComponent?.focus()}>Focus</button>
<button onclick={() => autocompleteComponent?.setRequestParams({ region: 'FR', language: 'fr' })}>
Switch to French
</button>
<button onclick={() => autocompleteComponent?.setOptions({ placeholder: 'Search locations...', debounce: 300 })}>
Update Options
</button>
| Method | Signature | Description |
|---|---|---|
clear() |
() => void |
Clears the input, removes suggestions, and resets the session token. |
focus() |
() => void |
Sets focus on the text input field. |
getRequestParams() |
() => RequestParams |
Returns the current internal requestParams object. |
setRequestParams(params) |
(params: Partial<RequestParams>) => void |
Dynamically updates request parameters. Useful for changing search criteria (region, language, location bias, etc.). Parameters are merged with existing ones. |
setFetchFields(fields) |
(fields: string[]) => void |
Dynamically updates the Place Data Fields to fetch when a place is selected. |
getFetchFields() |
() => string[] |
Returns the current array of Place Data Fields that will be requested. |
setOptions(options) |
(options: Partial<ComponentOptions>) => void |
Dynamically updates the component's configuration options. Merges the provided options with existing settings. |
getOptions() |
() => ComponentOptions |
Returns the current validated options used by the component. Useful for inspecting configuration settings. |
| Option | Type | Default | Description |
|---|---|---|---|
placeholder |
string |
'' |
Placeholder text for the input field. |
debounce |
number |
100 |
Delay in ms before firing API request. Set to 0 to disable. |
distance |
boolean |
true |
Show distance from requestParams.origin (if provided). |
distance_units |
'km' | 'miles' |
'km' |
Units for displaying distance. |
label |
string |
'' |
Optional label text displayed above the input. |
autofocus |
boolean |
false |
Automatically focus the input on mount. |
autocomplete |
string |
'off' |
The autocomplete attribute for the input field. |
classes |
Partial<ComponentClasses> |
{} |
Object to override default CSS classes. See Styling section. |
clear_input |
boolean |
true |
If false, retains the formattedAddress in the input after selection. |
The component includes built-in styles with .pac- prefixed CSS classes, providing a complete, accessible UI out of the box. These styles are:
options.classes propThe default styles include:
options.classes)Override any default styling by providing your own CSS classes via options.classes. Your custom classes will replace the default .pac- classes for the specified elements.
Available Class Keys:
section: The main container section (default: .pac-section)container: The div containing the input and suggestions list (default: .pac-container)label: The label elementinput: The main text input element (default: .pac-input)icon_container: Container for the optional icon (default: .pac-icon-container)icon: SVG string for the iconul: The <ul> element for the suggestions list (default: .pac-ul)li: Each <li> suggestion item (default: .pac-li)li_current: Class added to the currently highlighted <li> (default: .pac-li-current)li_button: The <button> within each list item (default: .pac-li-button)li_button_current: Class added to the currently highlighted button (default: .pac-li-button-current)li_div_container: Container div within each list item (default: .pac-li-div-container)li_div_one: First inner div containing the main text (default: .pac-li-div-one)li_div_one_p: The <p> tag containing the main suggestion text (default: .pac-li-div-one-p)li_div_one_p_secondaryText: The <p> tag for secondary text (default: .pac-li-div-one-p-secondaryText)li_div_p_container: Container for paragraphs (default: .pac-li-div-p-container)li_div_two: Second inner div containing the distance (default: .pac-li-div-two)li_div_two_p: The <p> tag containing the distance text (default: .pac-li-div-two-p)kbd_container: Container for the keyboard hint keys (default: .pac-kbd-container)kbd_escape: The <kbd> tag for the 'Esc' hint (default: .pac-kbd-escape)kbd_up: The <kbd> tag for the 'Up Arrow' hint (default: .pac-kbd-up)kbd_down: The <kbd> tag for the 'Down Arrow' hint (default: .pac-kbd-down)kbd_active: Class applied to active keyboard hints (default: .pac-kbd-active)map_pin_icon: SVG string for the map pin iconhighlight: The class applied to the <span> wrapping the matched text (default: .pac-highlight)Example - Using with Tailwind CSS:
const options = {
classes: {
input: 'w-full px-4 py-2.5 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500',
ul: 'absolute mt-1 w-full bg-white shadow-lg rounded-md border border-gray-200 max-h-60 overflow-auto',
li: 'px-4 py-2 hover:bg-blue-500 hover:text-white cursor-pointer',
li_current: 'bg-blue-500 text-white',
highlight: 'font-semibold text-blue-700'
}
};
Example - Using with Custom CSS:
const options = {
classes: {
section: 'autocomplete-wrapper',
input: 'search-input',
ul: 'suggestions-list',
li: 'suggestion-item',
li_current: 'suggestion-item--active'
}
};
onResponse: (response: PlaceResult) => voidfetchFields are retrieved.onError: (error: string) => voidThis component is fully written in TypeScript with comprehensive type definitions.
Component:
import { PlaceAutocomplete } from 'places-autocomplete-svelte';
Types and Interfaces:
import type {
PlaceResult, // Place data returned from API
ComponentOptions, // UI and behavior configuration
RequestParams, // Autocomplete request parameters
FormattedAddress, // Standardised address structure
ComponentClasses, // CSS class overrides
Props // Component props
} from 'places-autocomplete-svelte/interfaces';
Google Maps Loader Helpers:
import {
setGMapsContext, // Create shared context
getGMapsContext, // Retrieve context
hasGMapsContext, // Check if context exists
initialiseGMaps, // Initialise with context
initialiseGMapsNoContext, // Initialise standalone
importLibrary, // Load Google Maps libraries
type GMapsContext, // Context type
type APIOptions // Loader options type
} from 'places-autocomplete-svelte/gmaps';
fetchFields) are billed separately. Only request the fields you need to manage costs effectively.Need this functionality for a non-Svelte project? Check out our companion vanilla JavaScript library:
places-autocomplete-js - Same core Google Places (New) Autocomplete features, framework-agnostic implementation.
Contributions are welcome! We appreciate bug reports, feature requests, and pull requests.
How to contribute:
git checkout -b feature/amazing-feature)git commit -m 'Add amazing feature')git push origin feature/amazing-feature)Please ensure your code follows the existing style and includes appropriate tests.
Alexander Pechkarev
This project is licensed under the MIT License - see the LICENSE file for details.
Built with β€οΈ using Svelte 5 and Google Maps Platform