Billing & Shipping Address

E-commerce applications commonly require both billing and shipping addresses. This example demonstrates managing multiple autocomplete instances, implementing a "same as billing" feature, and synchronising form state across components.

Live Demo

Fill in the billing address, then try checking the "same as billing" option to see the shipping address auto-populate. Uncheck it to enter a different shipping address.

Billing Address

powered by
powered by Google

Shipping Address

powered by
powered by Google

How It Works

This example demonstrates several important patterns for managing multiple address inputs.

  1. Separate Component Instances: Each address (billing and shipping) has its own PlaceAutocomplete instance with its own response handler. This keeps the data flow clean and predictable.
  2. Shared Parsing Logic: Create a reusable parseAddressComponents function to extract address fields from the API response. This ensures consistency when populating both forms.
  3. Conditional Rendering: Use Svelte's {#if !sameAsBilling} block to show/hide the shipping address form based on the checkbox state.
  4. Address Synchronisation: When "same as billing" is checked, copy the billing address to shipping using the spread operator { ...billingAddress } to create an independent copy.
  5. State Management: Track both addresses independently in reactive state objects. This allows for validation, comparison, and separate submission to your backend.

Full Code Example

Here's a simplified version showing the core pattern. Extend this with validation, error handling, and backend integration as needed.

<script lang="ts">
	import { PlaceAutocomplete } from 'places-autocomplete-svelte';
	import type { PlaceResult } from 'places-autocomplete-svelte/interfaces';

	let billingAddress = $state({ /* address fields */ });
	let shippingAddress = $state({ /* address fields */ });
	let sameAsBilling = $state(false);

	const parseAddress = (response: PlaceResult) => {
		const getComponent = (type: string) =>
			response.addressComponents?.find(c => c.types.includes(type))?.longText || '';
		
		return {
			street_number: getComponent('street_number'),
			route: getComponent('route'),
			locality: getComponent('locality'),
			postal_code: getComponent('postal_code'),
			// ... other fields
		};
	};

	const onBillingResponse = (response: PlaceResult) => {
		billingAddress = parseAddress(response);
		if (sameAsBilling) {
			shippingAddress = { ...billingAddress };
		}
	};

	const onShippingResponse = (response: PlaceResult) => {
		shippingAddress = parseAddress(response);
	};

	const handleSameAsBilling = () => {
		if (sameAsBilling) {
			shippingAddress = { ...billingAddress };
		}
	};
</script>

<!-- Billing Address -->
<h2>Billing Address</h2>
<PlaceAutocomplete onResponse={onBillingResponse} />

<!-- Same as Billing Checkbox -->
<label>
	<input type="checkbox" bind:checked={sameAsBilling} onchange={handleSameAsBilling} />
	Shipping address same as billing
</label>

<!-- Shipping Address (conditionally shown) -->
{#if !sameAsBilling}
	<h2>Shipping Address</h2>
	<PlaceAutocomplete onResponse={onShippingResponse} />
{/if}