A Vue 3 autocomplete component for location search using OpenStreetMap's Nominatim API. This component provides a user-friendly interface for searching locations with debounced API calls, keyboard navigation, and customizable styling.
- Autocomplete search for locations using the Nominatim API.
- Supports keyboard navigation (
ArrowUp
,ArrowDown
,Enter
,Escape
,Tab
). - Accessible with ARIA attributes for screen reader support.
- Customizable styles and classes for flexible integration.
- Debounced API requests to optimize performance.
- Emits events for selection and errors.
Install the component and its dependency:
npm install vue-osm-autocomplete
Import and use the component in your Vue 3 application:
<template>
<OpenStreetMapAutocomplete
v-model="selectedLocation"
placeholder="Search for a place"
@search-error="handleError"
/>
</template>
<script setup>
import { ref } from "vue";
import OpenStreetMapAutocomplete from "vue-osm-autocomplete";
const selectedLocation = ref(null);
const handleError = (error) => console.error("Search error:", error);
</script>
Prop | Type | Default | Description |
---|---|---|---|
modelValue |
OpenStreetMapLocation | null |
null |
The selected location object (v-model). |
debounce |
number |
300 |
Debounce time for API requests (in milliseconds). |
placeholder |
string |
"Search location" |
Placeholder text for the input field. |
noResultsText |
string |
"No locations found" |
Text displayed when no search results are found. |
endpoint |
string |
"https://nominatim.openstreetmap.org/search" |
Nominatim API endpoint URL. |
language |
string |
"en" |
Language for API results (e.g., "en" , "fr" ). |
classes |
OpenStreetMapAutocompleteClasses |
undefined |
Custom CSS classes for component elements (see below). |
styles |
OpenStreetMapAutocompleteStyles |
undefined |
Inline styles for component elements (see below). |
interface OpenStreetMapLocation {
place_id: number;
licence: string;
osm_type: string;
osm_id: number;
lat: string;
lon: string;
category: string;
type: string;
place_rank: number;
importance: number;
addresstype: string;
name: string;
display_name: string;
boundingbox: string[];
}
interface OpenStreetMapAutocompleteClasses {
root?: string;
form?: string;
inputWrapper?: string;
input?: string;
divider?: string;
button?: string;
dropdown?: string;
options?: string;
option?: string;
empty?: string;
loading?: string;
}
interface OpenStreetMapAutocompleteStyles {
root?: Record<string, string>;
form?: Record<string, string>;
inputWrapper?: Record<string, string>;
input?: Record<string, string>;
divider?: Record<string, string>;
button?: Record<string, string>;
dropdown?: Record<string, string>;
options?: Record<string, string>;
option?: Record<string, string>;
empty?: Record<string, string>;
loading?: Record<string, string>;
}
Event | Payload | Description |
---|---|---|
update:modelValue |
OpenStreetMapLocation | null |
Emitted when a location is selected. |
search-error |
unknown |
Emitted when an API error occurs. |
Slot | Description |
---|---|
icon |
Custom icon for the search button. |
<template>
<OpenStreetMapAutocomplete v-model="selectedLocation">
<template #icon>
<svg viewBox="0 0 24 24">
<path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z" />
</svg>
</template>
</OpenStreetMapAutocomplete>
</template>
The component uses scoped CSS with the following class structure:
.autocomplete
: Root container..autocomplete-form
: Form element..autocomplete-input-wrapper
: Input wrapper..autocomplete-input
: Input element..autocomplete-divider
: Divider between input and button..autocomplete-button
: Search button..autocomplete-dropdown
: Dropdown container..autocomplete-options
: Options list..autocomplete-option
: Individual option (with.is-highlighted
and.is-selected
modifiers)..autocomplete-empty
: No results message..autocomplete-loading
: Loading spinner.
You can override styles using the classes
or styles
props:
<template>
<OpenStreetMapAutocomplete
v-model="selectedLocation"
:classes="{ root: 'custom-autocomplete', input: 'custom-input' }"
:styles="{ root: { backgroundColor: 'lightgray' } }"
/>
</template>
<style>
.custom-autocomplete {
border-radius: 8px;
}
.custom-input {
border: 2px solid blue;
}
</style>
- Supports ARIA attributes (
role="combobox"
,aria-expanded
,aria-controls
, etc.). - Keyboard navigation with
ArrowUp
,ArrowDown
,Enter
,Escape
, andTab
. - Dropdown closes on outside click or
Escape
key.
- Nominatim Usage Policy: The component uses OpenStreetMap's Nominatim API, which has strict usage limits (max 1 request per second, no heavy load). For production applications, consider:
- Using a custom
endpoint
with a Nominatim instance or proxy. - Implementing client-side caching or rate-limiting on your end.
- Reviewing the Nominatim usage policy.
- Using a custom
- Dependencies: Requires
@vueuse/core
for debouncing API requests. - Minimum Query Length: The component requires a minimum of 3 characters before making API requests.
- Error Handling: Errors are emitted via the
search-error
event. Implement custom error handling in your application if needed.
<template>
<OpenStreetMapAutocomplete
v-model="selectedLocation"
@search-error="handleError"
/>
</template>
<script setup>
import { ref } from "vue";
import OpenStreetMapAutocomplete from "vue-osm-autocomplete";
const selectedLocation = ref(null);
const handleError = (error) => {
alert("Failed to load locations: " + error.message);
};
</script>
Contributions are welcome! Please submit issues or pull requests to the GitHub repository.
MIT License