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.

AddressValidation displayed in a modal
What you’ll edit or create
Section titled “What you’ll edit or create”- Edit the addresses block —
blocks/commerce-addresses/commerce-addresses.js - Edit the
AddressValidationcontainer - Edit the
Addressescontainer - Create the modal helper —
blocks/commerce-addresses/modal-utils.js
What you’ll build
Section titled “What you’ll build”By the end of this tutorial, you’ll have:
- A modal in your
commerce-addressesblock that renders theAddressValidationcontainer when your verification service returns a suggested address. - An
onSubmithandler on theAddressescontainer that calls your verification service before saving, usingforwardFormRefandonAddressDatato read the submitted form values. - A
modal-utils.jshelper that opens and closes the validation modal.
Integration in commerce-addresses block
Section titled “Integration in commerce-addresses block”Update your blocks/commerce-addresses/commerce-addresses.js to:
- Import
AddressValidation - Pass
forwardFormRef,onAddressData, andonSubmittoAddresses - When a suggestion exists, show a modal with
AddressValidationand two actions: Edit Address and Save Address
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);}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;};Optional styles
Section titled “Optional styles”/* Minimal layout for the modal */.modal-content { padding: 32px;}
.actions-container { display: flex; gap: 16px; margin-top: 48px; justify-content: flex-end;}Next steps
Section titled “Next steps”- See the
AddressValidationcontainer container for props and behaviors. - Ensure your suggestion matches the
CustomerAddressInputshape expected by the Addresses container. - See Validate a shipping address to add the same confirmation step before a shopper places an order at checkout.