Skip to content
Tutorials

Validate a saved address

When your address verification service returns a suggested address that differs from what a shopper entered, you can let them choose between the two before saving. This tutorial shows you how to add that confirmation step using the AddressValidation container.

Validate address

AddressValidation displayed in a modal
  • Edit the addresses block — blocks/commerce-addresses/commerce-addresses.js
  • Edit the AddressValidation container
  • Edit the Addresses container
  • Create the modal helper — blocks/commerce-addresses/modal-utils.js

By the end of this tutorial, you’ll have:

  • A modal in your commerce-addresses block that renders the AddressValidation container when your verification service returns a suggested address.
  • An onSubmit handler on the Addresses container that calls your verification service before saving, using forwardFormRef and onAddressData to read the submitted form values.
  • A modal-utils.js helper that opens and closes the validation modal.

Update your blocks/commerce-addresses/commerce-addresses.js to:

  • Import AddressValidation
  • Pass forwardFormRef, onAddressData, and onSubmit to Addresses
  • When a suggestion exists, show a modal with AddressValidation and two actions: Edit Address and Save Address
blocks/commerce-addresses/commerce-addresses.js
import { Addresses } from '/@dropins/storefront-account/containers/Addresses.js';
import AddressValidation from '/@dropins/storefront-account/containers/AddressValidation.js';
import { render as accountRenderer } from '/@dropins/storefront-account/render.js';
import { Button, provider as UI } from '/@dropins/tools/components.js';
import { readBlockConfig } from '/scripts/aem.js';
import {
CUSTOMER_ADDRESS_PATH,
CUSTOMER_LOGIN_PATH,
checkIsAuthenticated,
rootLink,
} from '/scripts/commerce.js';
import { showModal, removeModal } from '/modal-utils.js';
const validateAddress = async (address) => {
// Call your service with the form data
// const result = await fetch(...)
// return result.suggestedAddress || null
// Example mocked suggestion
return {
city: 'Bainbridge Island',
countryCode: 'US',
postcode: '98110-2450',
region: { regionCode: 'WA', regionId: 1 },
street: ['123 Winslow Way E'],
telephone: '555-123-4567',
};
};
const finalizeSubmission = (setContainerProps, formRef, updates = {}) => {
setContainerProps({ onSubmit: null, ...updates });
setTimeout(() => formRef.current?.requestSubmit(), 1);
};
const renderAddressValidation = async ({
originalAddress,
suggestedAddress,
onEdit,
onSave,
}) => {
const container = document.createElement('div');
const addressValidation = document.createElement('div');
const actionsContainer = document.createElement('div');
const editAddressButton = document.createElement('div');
const saveAddressButton = document.createElement('div');
actionsContainer.appendChild(editAddressButton);
actionsContainer.appendChild(saveAddressButton);
container.appendChild(addressValidation);
container.appendChild(actionsContainer);
actionsContainer.classList.add('actions-container');
await showModal(container);
let selectedAddress, currentSelection;
provider.render(AddressValidation, {
selectedAddress: 'suggested',
originalAddress,
suggestedAddress,
handleSelectedAddress: async ({ address, selection }) => {
selectedAddress = address;
currentSelection = selection;
},
})(addressValidation);
UI.render(Button, {
children: 'Edit Address',
size: 'medium',
variant: 'secondary',
onClick: () => {
if (currentSelection === 'suggested') {
onEdit(selectedAddress);
}
removeModal();
},
})(editAddressButton);
UI.render(Button, {
children: 'Save Address',
size: 'medium',
variant: 'primary',
onClick: () => {
const updates = {};
if (currentSelection === 'suggested') {
updates.inputsDefaultValueSet = selectedAddress;
}
removeModal();
onSave(updates);
},
})(saveAddressButton);
};
export default async function decorate(block) {
const { 'minified-view': minifiedViewConfig = 'false' } = readBlockConfig(block);
if (!checkIsAuthenticated()) {
window.location.href = rootLink(CUSTOMER_LOGIN_PATH);
return;
}
let originalAddress;
const formRef = { current: null };
const addressesContainer = await accountRenderer.render(Addresses, {
minifiedView: minifiedViewConfig === 'true',
withActionsInMinifiedView: false,
withActionsInFullSizeView: true,
routeAddressesPage: () => rootLink(CUSTOMER_ADDRESS_PATH),
forwardFormRef: formRef,
onAddressData: (values) => {
originalAddress = values?.data;
},
onSubmit: async (event, formValid) => {
if (!formValid) return;
const setContainerProps = (updates) => {
addressesContainer.setProps((prev) => ({ ...prev, ...updates }));
};
const suggestedAddress = await validateAddress(originalAddress);
if (!suggestedAddress) {
finalizeSubmission(setContainerProps, formRef);
return;
}
await renderAddressValidation({
originalAddress,
suggestedAddress,
onEdit: (selectedAddress) => {
setContainerProps({ inputsDefaultValueSet: selectedAddress });
},
onSave: (updates) => {
finalizeSubmission(setContainerProps, formRef, updates);
},
});
},
})(block);
}
modal-utils.js
import createModal from '/modal/modal.js';
let modal;
export const showModal = async (content) => {
modal = await createModal([content]);
modal.showModal();
};
export const removeModal = () => {
if (!modal) return;
modal.removeModal();
modal = null;
};
/* Minimal layout for the modal */
.modal-content {
padding: 32px;
}
.actions-container {
display: flex;
gap: 16px;
margin-top: 48px;
justify-content: flex-end;
}
  • See the AddressValidation container container for props and behaviors.
  • Ensure your suggestion matches the CustomerAddressInput shape expected by the Addresses container.
  • See Validate a shipping address to add the same confirmation step before a shopper places an order at checkout.