Address autocomplete in Vue
About 15 minutes. Updated 11 September 2026.
A single file Vue 3 component that adds Australian address autocomplete to a form with the Locio API: script setup, a watched query with a debounce, request cancellation, and the picked address emitted to the parent.
One single file component. The query is a ref, a watcher debounces it and makes the request, and the picked address is emitted upward as a typed object so the parent can store the address id and use the coordinates.
01The component
The watcher does the work that usually goes wrong: it clears the previous timer and aborts the previous request on every change, so a slow answer for an earlier prefix cannot arrive after a newer one. Results come from /v1/addresses, and each is a resolved address.
<!-- AddressSearch.vue -->
<script setup lang="ts">
import { ref, watch } from "vue";
type Address = {
// The id in the register the address came from. In Australia it is the
// G-NAF Address Detail PID, sent as address_detail_pid as well.
id: string;
formatted: string;
lat: number;
lng: number;
mesh_block?: string; // Australian records only
components: Record<string, string>;
};
const KEY = import.meta.env.VITE_LOCIO_KEY as string; // a public key
const API = "https://api.locio.com.au";
const emit = defineEmits<{ pick: [address: Address] }>();
const query = ref("");
const results = ref<Address[]>([]);
const picked = ref<Address | null>(null);
const busy = ref(false);
let timer: ReturnType<typeof setTimeout> | undefined;
let inflight: AbortController | undefined;
watch(query, (value) => {
clearTimeout(timer);
inflight?.abort();
picked.value = null;
const q = value.trim();
if (q.length < 3) {
results.value = [];
return;
}
// 200ms of quiet before asking: one request per pause, not per keystroke.
timer = setTimeout(() => search(q), 200);
});
async function search(q: string) {
inflight = new AbortController();
busy.value = true;
try {
const url = new URL(`${API}/v1/addresses`);
url.searchParams.set("q", q);
url.searchParams.set("limit", "8");
const res = await fetch(url, {
headers: { Authorization: `Bearer ${KEY}` },
signal: inflight.signal,
});
// country_code says which country was searched; note says why the list
// is empty when empty is the right answer.
if (res.ok) {
const body = (await res.json()) as { data: Address[]; country_code?: string; note?: string };
results.value = body.data;
}
} catch (err) {
if ((err as Error).name !== "AbortError") console.error(err);
} finally {
busy.value = false;
}
}
function pick(address: Address) {
picked.value = address;
query.value = address.formatted;
results.value = [];
emit("pick", address);
}
</script>
<template>
<div>
<label for="address">Delivery address</label>
<input id="address" v-model="query" type="search" autocomplete="off" placeholder="145 sydney road" />
<p v-if="busy">Searching…</p>
<ul v-if="results.length && !picked" role="listbox">
<li v-for="address in results" :key="address.id">
<button type="button" @click="pick(address)">{{ address.formatted }}</button>
</li>
</ul>
<!-- The id is what your form should submit, not the text. -->
<input type="hidden" name="address_id" :value="picked?.id ?? ''" />
</div>
</template>Store id. It is the address's id in its country's register, which in Australia is the G-NAF Address Detail PID, sent under address_detail_pid as well. It is stable across releases for an address that has not changed, where the formatted line is not, so it is the thing to key your own records on rather than the text.
02Use it
<script setup lang="ts">
import AddressSearch from "./AddressSearch.vue";
</script>
<template>
<form method="post" action="/checkout">
<AddressSearch @pick="(address) => console.log(address.id, address.lat, address.lng)" />
<button type="submit">Continue</button>
</form>
</template>What it costs
One unit per request. With the debounce, a completed address is usually three or four requests, so the free 10,000 a month covers a few thousand addresses. Refused requests are free.
Related
- Address autocomplete in plain JavaScript
- Address autocomplete in React with the Locio package
- Address autocomplete in React
- Address autocomplete in Vue with the Locio package
- Stand up a React site with Australian address search
- Normalise shipping addresses at checkout
- Put a searched address on a map with MapLibre
- Validate and geocode Australian addresses in PHP
- Australian address autocomplete for WooCommerce
- Address validation and geocoding in Go
- Use Locio from Claude Code, Claude Desktop and Cursor
- Give an agent address tools with the Anthropic API
- Clean a spreadsheet of addresses with an agent
- The API reference, with every call in curl, Node, Python, Ruby, Go and PHP.