A flexible, accessible, and secure Svelte component leveraging the Google Maps Places Autocomplete API (New).
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 that prevents conflicts with other map components on the same page.
Need this functionality for a non-Svelte project? Check out our companion vanilla JavaScript library, places-autocomplete-js
, which offers the same core Google Places (New) Autocomplete features.
View places-autocomplete-js
on GitHub
- Integrates with the modern Google Maps Places Autocomplete API (New).
- Automatic Shared Loader: Intelligently creates a single Google Maps loader instance and shares it via Svelte's context.
- Highly Accessible: Follows WAI-ARIA patterns for comboboxes, with full keyboard navigation and screen reader support.
- Secure: Safely renders suggestions to protect against XSS attacks.
- Automatically handles session tokens for cost management.
- Debounced Input: Limits API calls while the user is typing (configurable).
- Suggestion Highlighting: Automatically highlights the portion of text matching the user's input.
- Imperative API: Exposes
clear()
,focus()
, andgetRequestParams()
methods for direct control. - Customisable Styling: Easily override default styles using the
options.classes
prop. - TypeScript Support: Fully written in TypeScript with included type definitions.
- Event Handling: Provides
onResponse
andonError
callbacks.
See a live demo of the component in action: Basic Example
Reactive parameters - change the search criteria based on user input, like filtering by country or change results language.
Customise request parameters - construct a requestParams
object and control various aspects of the search, including language, region, and more.
Retain Input Value After Selection - This example demonstrates how to configure the component to keep the selected address visible in the input field after a suggestion is chosen.
- Google Maps API Key with the "Places API" enabled. Refer to Use API Keys for detailed instructions.
npm install places-autocomplete-svelte
# or
yarn add places-autocomplete-svelte
The PlaceAutocomplete
component is designed for simplicity. Here’s the minimum required to get it working:
// In your +page.svelte or a parent component
<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;
// --- Handle Component Response ---
const handleResponse = (response: PlaceResult) => {
console.log('Place Selected:', response.formattedAddress);
};
// --- Handle Component Errors ---
const handleError = (error: string) => {
console.error('Error:', error);
};
</script>
<!-- Add the component to your page -->
<PlaceAutocomplete {onResponse} {onError} {PUBLIC_GOOGLE_MAPS_API_KEY} />
The PlaceAutocomplete
component intelligently manages the Google Maps loader. For simple use cases, it handles loading automatically. However, if you need to use other Google Maps libraries (like maps
or marker
) on the same page, you should initialise the loader once in a parent component (+page.svelte
or +layout.svelte
). This prevents conflicts and ensures all libraries are loaded efficiently.
The component provides helper functions (setGMapsContext
, getGMapsContext
, initialiseGMaps
) to manage this.
Here is how you would set it up in your +page.svelte
:
// src/routes/+page.svelte
<script lang="ts">
import { onMount } from 'svelte';
import { browser } from '$app/environment';
import { PlaceAutocomplete } from 'places-autocomplete-svelte';
import { initialiseGMaps, setGMapsContext, getGMapsContext } from 'places-autocomplete-svelte/gmaps';
// 1. Set the context for Google Maps. This should be done in the script's top-level scope.
setGMapsContext();
// 2. If we are in the browser, trigger the asynchronous loading process.
if (browser) {
initialiseGMaps({
key: import.meta.env.VITE_PUBLIC_GOOGLE_MAPS_API_KEY,
v: 'weekly'
}).catch((error: any) => {
// ... handle error (already logged in initialiseGMaps)
});
}
// ... rest of your component logic
</script>
<!-- The PlaceAutocomplete component will automatically use the context -->
<!-- You do NOT need to pass the `PUBLIC_GOOGLE_MAPS_API_KEY` prop to the component if you initialise the loader in a parent component.-->
<PlaceAutocomplete onResponse={...} onError={...} />
<!-- You can now use other Google Maps services, e.g., a map -->
<div id="map"></div>
Your Google Maps API Key is a sensitive credential. To prevent unauthorised use and unexpected charges, you must restrict it.
- Go to the Google Cloud Console.
- Select your API key.
- Under Application restrictions, select HTTP referrers (web sites) and add your application's domain(s) (e.g.,
your-domain.com/*
). - Under API restrictions, select Restrict key and choose the APIs you are using (e.g., Places API, Maps JavaScript API).
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.
- Keyboard Navigation: Users can navigate suggestions using
ArrowUp
,ArrowDown
, select withEnter
, and close the list withEscape
. - Screen Reader Support: Uses
role="combobox"
,aria-autocomplete
,aria-expanded
, andaria-activedescendant
to provide a clear experience for screen reader users. - Focus Management: Focus remains on the input field while navigating the suggestion list.
Prop | Type | Required | Default | Description |
---|---|---|---|---|
PUBLIC_GOOGLE_MAPS_API_KEY |
string |
Yes | - | Your restricted Google Maps API Key. Not required if the loader has been initialised in a parent component. |
fetchFields |
string[] |
No | ['formattedAddress', 'addressComponents'] |
Place Data Fields to request. Affects API cost. |
requestParams |
Partial<RequestParams> |
No | { inputOffset: 3, ... } |
Parameters for the Autocomplete API request. |
options |
Partial<ComponentOptions> |
No | { debounce: 100, ... } |
Options to control component behavior and appearance. |
onResponse |
(response: PlaceResult) => void |
Yes | - | Callback triggered with the selected place details. |
onError |
(error: string) => void |
Yes | - | Callback triggered when an error occurs. |
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>
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. |
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. |
Customise the component by providing your own CSS classes via options.classes
.
Available Class Keys:
section
: The main containersection
.container
: Thediv
containing the input and suggestions list.label
: Thelabel
element.input
: The main textinput
element.icon_container
: Container for the optional icon.icon
: SVG string for the icon.ul
: The<ul>
element for the suggestions list.li
: Each<li>
suggestion item.li_current
: Class added to the currently highlighted<li>
.li_div_container
: Containerdiv
within each list item.li_div_one
: First innerdiv
(contains the main text).li_div_one_p
: The<p>
tag containing the main suggestion text.li_div_two
: Second innerdiv
(contains the distance).li_div_two_p
: The<p>
tag containing the distance text.kbd_container
: Container for the keyboard hint keys.kbd_escape
: The<kbd>
tag for the 'Esc' hint.kbd_up
: The<kbd>
tag for the 'Up Arrow' hint.kbd_down
: The<kbd>
tag for the 'Down Arrow' hint.highlight
: The class applied to the<span>
wrapping the matched text. Defaults to'font-bold'
.
Example:
const options = {
classes: {
input: 'form-input w-full rounded-md shadow-sm',
ul: 'absolute bg-white shadow-lg rounded-md mt-1 w-full z-10',
li_current: 'bg-blue-500 text-white',
highlight: 'text-blue-700 font-semibold'
}
};
onResponse
:(response: PlaceResult) => void
- Fired after a user selects a suggestion and
fetchFields
are retrieved.
- Fired after a user selects a suggestion and
onError
:(error: string) => void
- Fired on any error (API loading, fetching suggestions, etc.).
This component is written in TypeScript. Import types from places-autocomplete-svelte/interfaces
and helpers from places-autocomplete-svelte/gmaps
.
- This component uses the Google Maps JavaScript API (Places library). Usage is subject to Google's terms and pricing.
- It uses Session Tokens automatically to group Autocomplete requests, which can reduce costs.
- Place Details requests (via
fetchFields
) are billed separately. Only request the fields you need to manage costs.
Contributions are welcome! Please feel free to open an issue or submit a pull request.