@salla.sa/ui-address-autocomplete-widget
v1.2.1
Published
Framework-agnostic address autocomplete web component using Lit
Readme
@salla.sa/ui-address-autocomplete-widget
A framework-agnostic address autocomplete web component built with Lit and Google Places API. Works seamlessly with React, Vue, Angular, or vanilla JavaScript.
🚀 Default Configuration
The component is pre-configured to work with Salla's API (api.salla.dev) out of the box. No additional proxy setup needed!
Default Endpoints:
- Autocomplete:
https://api.salla.dev/store/v1/checkout/address/autocomplete/query - Details:
https://api.salla.dev/store/v1/checkout/address/autocomplete/details
Expected Response Format:
All responses must be wrapped in the standard Salla API format:
// Autocomplete response
{
"success": true,
"status": 200,
"data": [
{
"id": "ChIJOwg_06VPwokRYv534QaPC8g",
"formatted_address": "Empire State Building, New York, NY, USA",
"description": "Empire State Building",
"types": ["establishment", "point_of_interest"]
}
]
}
// Details response
{
"success": true,
"status": 200,
"data": {
"id": "ChIJOwg_06VPwokRYv534QaPC8g",
"formatted_address": "20 W 34th St., New York, NY 10001, USA",
"address_components": [
{
"long_name": "20",
"short_name": "20",
"types": ["street_number"]
}
// ... more components
],
"geometry": {
"location": { "lat": 40.748817, "lng": -73.985428 }
},
"types": ["street_address"]
}
}
// Error response
{
"success": false,
"status": 400,
"message": "Input is required"
}You can override the proxy-url prop if you want to use your own backend proxy (must follow the same response format).
Why Backend Proxy?
- ✅ No CORS issues - API calls from server-side
- ✅ API key hidden from users
- ✅ Full control over security & rate limiting
- ✅ No external SDK dependencies (~100KB saved)
See CORS_EXPLANATION.md for detailed explanation.
Features
✅ Framework Agnostic - Works with any framework or vanilla JS
✅ Full TypeScript Support - Complete type definitions included
✅ No SDK Required - Uses REST API via backend proxy (no Google SDK loading)
✅ Session Token Management - Optimized billing with automatic session handling
✅ Address Validation Support - Detects if validation is available for the country
✅ UX Best Practices - Keyboard navigation, loading states, error handling
✅ Customizable - Extensive props for location bias, country restrictions, and more
✅ Highlighted Matches - Visual highlighting of matched text in suggestions
✅ Throttled Requests - Efficient API usage with request throttling
Installation
npm install @salla.sa/ui-address-autocomplete-widgetUsage
Quick Start (Using Salla API)
The component works immediately with Salla's backend API:
<!DOCTYPE html>
<html>
<head>
<script type="module">
import '@salla.sa/ui-address-autocomplete-widget';
const autocomplete = document.querySelector('salla-address-autocomplete');
// Optional: Set auth token if required by your Salla integration
autocomplete.authToken = 'your-salla-auth-token';
autocomplete.addEventListener('place-select', (e) => {
console.log('Selected place:', e.detail.place);
console.log('Validation support:', e.detail.validationSupport);
});
</script>
</head>
<body>
<salla-address-autocomplete
placeholder="Enter an address..."
label="Delivery Address"
></salla-address-autocomplete>
</body>
</html>Using Custom Backend Proxy
If you want to use your own backend proxy instead of Salla's API:
<salla-address-autocomplete
proxy-url="http://localhost:3001/api/places"
placeholder="Enter an address..."
label="Delivery Address"
></salla-address-autocomplete>See proxy-examples/ for complete backend setup guide.
React
import '@salla.sa/ui-address-autocomplete-widget';
function CheckoutForm() {
const [authToken, setAuthToken] = useState('');
const handlePlaceSelect = (e: CustomEvent) => {
const { place, validationSupport } = e.detail;
console.log('Selected:', place);
console.log('Country:', validationSupport.countryCode);
};
return (
<salla-address-autocomplete
auth-token={authToken}
placeholder="Enter delivery address..."
label="Address"
included-region-codes={JSON.stringify(['US', 'CA'])}
onplace-select={handlePlaceSelect}
/>
);
}Vue 3
<template>
<salla-address-autocomplete
:auth-token="authToken"
placeholder="Enter address..."
label="Address"
:included-region-codes="['SA', 'AE']"
@place-select="handlePlaceSelect"
/>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import '@salla.sa/ui-address-autocomplete-widget';
const authToken = ref('');
const handlePlaceSelect = (e: CustomEvent) => {
console.log('Selected place:', e.detail.place);
};
</script>Angular
// app.module.ts
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import '@salla.sa/ui-address-autocomplete-widget';
@NgModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class AppModule {}
// component.html
<salla-address-autocomplete
[attr.auth-token]="authToken"
placeholder="Enter address..."
label="Address"
(place-select)="handlePlaceSelect($event)"
></salla-address-autocomplete>
// component.ts
authToken = '';
handlePlaceSelect(event: CustomEvent) {
console.log('Selected:', event.detail.place);
}Properties
| Property | Type | Default | Description |
|----------|------|---------|-------------|
| proxyUrl | string | 'https://api.salla.dev' | Base URL for proxy server |
| authToken | string\|null | null | Optional bearer token for proxy authentication |
| placeholder | string | '' | Input placeholder text |
| value | string | '' | Initial/controlled value |
| label | string\|null | null | Label text above input |
| className | string | '' | CSS class for wrapper element |
| country | string\|string[]\|null | null | Restrict to specific countries. Accepts string ("SA" or "SA,AE") or array (['SA', 'AE']) |
| locationBias | object\|null | null | Bias results to location (radius + center) |
| locationRestriction | object\|null | null | Restrict results to location bounds |
| types | string[]\|null | null | Filter by place types (e.g., ['geocode', 'establishment']) |
| fields | string[] | ['formatted_address', 'geometry'] | Place data fields to fetch (cost optimized) |
| strictBounds | boolean | false | Only return results within bounds |
| useSessionToken | boolean | true | Enable session tokens (UUID v4) for billing optimization |
| onValidateAddress | function\|null | null | Backend validation callback |
| debounceDelay | number | 300 | Milliseconds to wait after typing stops |
| minChars | number | 3 | Minimum characters before search |
Default Fields (Cost Optimized)
[
'formatted_address', // Full address string
'geometry' // Lat/lng coordinates
]Note: Default configuration fetches only essential fields to minimize API costs (~$5/1000 requests).
To include country detection and validation support, add address_components:
autocomplete.fields = ['formatted_address', 'geometry', 'address_components'];Events
place-select
Fired when a user selects an address from the dropdown.
interface PlaceSelectEvent {
detail: {
place: PlaceDetails; // Complete place object with requested fields
validationSupport: {
isSupported: boolean | null; // true/false/null (if unknown)
countryCode: string | null; // ISO country code
};
};
}Example:
autocomplete.addEventListener('place-select', (e) => {
const { place, validationSupport } = e.detail;
if (validationSupport.isSupported) {
// Address Validation API is supported for this country
// You can use validateAddress() method
}
console.log('Address:', place.formatted_address);
console.log('Coordinates:', place.geometry.location);
console.log('Country:', validationSupport.countryCode);
});place_changed
Alias for place-select (for backward compatibility).
input
Fired on every input change.
interface InputEvent {
detail: {
value: string;
};
}Methods
validateAddress(place: PlaceDetails)
Validates an address using your backend API. Requires onValidateAddress callback.
const autocomplete = document.querySelector('salla-address-autocomplete');
autocomplete.onValidateAddress = async ({ place, sessionToken, validationSupport }) => {
// Call your backend API
const response = await fetch('/api/validate-address', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
address: place.formatted_address,
sessionToken,
countryCode: validationSupport.countryCode
})
});
return response.json();
};
// Later, when user submits the form:
autocomplete.addEventListener('place-select', async (e) => {
const result = await autocomplete.validateAddress(e.detail.place);
console.log('Validation result:', result);
});getValidationSupport(place: PlaceDetails)
Returns validation support information for a place.
const support = autocomplete.getValidationSupport(place);
console.log(support); // { isSupported: true, countryCode: 'US' }Advanced Examples
Country-Restricted Search
<salla-address-autocomplete
placeholder="Enter address in Saudi Arabia..."
country="SA"
></salla-address-autocomplete>
<!-- OR using JavaScript for multiple countries: -->
<salla-address-autocomplete
id="multi-country"
placeholder="Enter address in Saudi Arabia or UAE..."
></salla-address-autocomplete>
<script>
const autocomplete = document.getElementById('multi-country');
autocomplete.country = ['SA', 'AE'];
// OR: autocomplete.country = 'SA,AE';
</script>Location Bias (Prefer nearby results)
autocomplete.locationBias = {
radius: 50000, // 50km radius
center: { lat: 24.7136, lng: 46.6753 } // Riyadh coordinates
};Custom Fields (Cost Optimization)
// Minimal fields for cost savings
autocomplete.fields = ['formatted_address', 'geometry'];
// With address components for validation
autocomplete.fields = [
'formatted_address',
'address_components',
'geometry',
'types'
];Address Validation with Backend
autocomplete.onValidateAddress = async ({ place, sessionToken, validationSupport }) => {
if (!validationSupport.isSupported) {
return { valid: true, message: 'Validation not available for this country' };
}
const response = await fetch('https://your-api.com/validate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
address: place.formatted_address,
sessionToken
})
});
return response.json();
};Using Bearer Token Authentication
If your proxy requires authentication, use the authToken prop:
<salla-address-autocomplete
proxy-url="https://your-api.com/places"
auth-token="your-bearer-token-here"
placeholder="Enter address..."
></salla-address-autocomplete>JavaScript:
const autocomplete = document.querySelector('salla-address-autocomplete');
autocomplete.authToken = await getAuthToken(); // Get token from your auth systemReact:
<salla-address-autocomplete
proxy-url="https://your-api.com/places"
auth-token={authToken}
placeholder="Enter address..."
/>Styling
The component uses Shadow DOM with default styles. You can customize via CSS custom properties:
salla-address-autocomplete {
--input-border-color: #d1d5db;
--input-focus-color: #3b82f6;
--dropdown-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
}Or provide custom styles via className:
<salla-address-autocomplete
class-name="my-custom-class"
></salla-address-autocomplete>Google Places API Setup
- Go to Google Cloud Console
- Enable Places API and Geocoding API
- Create an API key with restrictions
- Add HTTP referrer restrictions for security
Cost Optimization Tips
- Use session tokens (
useSessionToken: true) - Groups requests for better pricing - Smart place details optimization - Automatically skips details API call when location data is already in autocomplete response (saves 1 API call per selection when data is available)
- Fetch only needed fields - Each field tier has different pricing (default is cost-optimized)
- Use address validation - Free autocomplete in 53 countries when using validation API
- Restrict by country - Reduces irrelevant results
- Debounce requests - Built-in 300ms debouncing reduces API calls by ~93%
- Minimum characters - Default 3 characters before search starts
Browser Support
- Chrome/Edge 90+
- Firefox 88+
- Safari 14+
- All modern browsers with Custom Elements v1 support
TypeScript Support
Full TypeScript definitions included:
import type {
PlaceDetails,
AutocompletePrediction,
ValidationSupport,
LocationBias
} from '@salla.sa/ui-address-autocomplete-widget';License
MIT
Support
For issues and questions, please visit: GitHub Issues
