Customer Address Management

When a customer checks out with a new address, the address is added to their address book.

If the customer is a guest, they have no need to manage an address book.

Customers can only add and remove addresses from the front end while they are logged in.

See the Customer model (opens new window) to learn about the methods available to retrieve customer address data e.g. Customer::getPrimaryBillingAddress() (opens new window), Customer::getPrimaryShippingAddress() (opens new window) and Customer::getAddressById() (opens new window).

# Get all the current customer’s addresses

{% set addresses = craft.commerce.customers.customer.addresses %}
{% for address in addresses %}
    {{ address.firstName }}<br/>
    {# ... #}
{% endfor %}

See craft\commerce\models\Address (opens new window) to learn about the fields available on an address.

# Get a current customer’s address by its ID

{% set address = craft.commerce.customers.customer.getAddressById(id) %}

# Creating or updating a customer address

The form action for adding or updating a customer’s address is commerce/customer-addresses/save.

This example would add a new address for the customer with the details in the address form field:

<form method="post">
    <input type="hidden" name="action" value="commerce/customer-addresses/save">
    {{ redirectInput('commerce/customer/addresses') }}
    {{ csrfInput() }}
    <input type="text" name="address[firstName]" value="{{ address is defined ? address.firstName : '' }}">
    <input type="text" name="address[lastName]" value="{{ address is defined ? address.lastName : '' }}">
    {# ... #}
    <input type="submit" value="Save"/>

To update an existing address, include its ID for the value of a address[id] parameter:


<form method="post">
    <input type="hidden" name="action" value="commerce/customer-addresses/save">
    {{ redirectInput('commerce/customer/addresses') }}
    {{ csrfInput() }}
    <input type="text" name="address[id]" value="{{ }}">
    <input type="text" name="address[firstName]" value="{{ address.firstName }}">
    <input type="text" name="address[lastName]" value="{{ address.lastName }}">
    {# ... #}
    <input type="submit" value="Save"/>

# Deleting a customer's address

The form action for deleting a customer address is commerce/customer-addresses/delete. All that’s needed is the address ID:

<form method="post">
    <input type="hidden" name="action" value="commerce/customer-addresses/delete">
    {{ redirectInput('commerce/customer/addresses') }}
    {{ csrfInput() }}
    <input type="hidden" name="id" value="{{ }}"/>
    <input type="submit" value="delete"/>

If an address is designated for shipping or billing in a cart, edits will carry over to the cart before checkout. Deleting an address will remove it from the cart and require further user action to complete the order.

# Validating addressses

Commerce saves customer address data without any validation. If you’d like to provide your own validation rules, you either do that on the front end or use a custom plugin or module to provide server side validation.

If you’d like to provide your own server side validation, make sure you’re comfortable creating a plugin or module for Craft CMS (opens new window).

If you write your own plugin or module, you’ll want to use its init() method to subscribe to the event that’s triggered when the Address model collects it rules prior to attempting validation. Your event listener can add additional validation rules (opens new window) for the Address model.

In this example, a new rule is added to make firstName and lastName required:

use craft\commerce\models\Address;
use craft\base\Model;
use craft\events\DefineRulesEvent;

    function(DefineRulesEvent $event) {
        $rules = $event->rules;
        $rules[] = [['firstName', 'lastName'], 'required'];
        $event->rules = $rules;

In any of the above examples that post to the commerce/customer-addresses/save action, you would access validation errors in your template like any other model:

{% if address %}
  {# gets all validation errors for the address #}
  {% set errors = address.getErrors() %}

  {# gets the `firstName` error for the address #}
  {% set firstNameErrors = address.getErrors('firstName') %}
{% endif %}

For a complete template example that outputs individual field validation errors, see shop/_includes/addresses/form.twig (opens new window) in the example templates.