# Billing address can match shipping without re-entry

- **Pattern:** `ab-001106` (`ecommerce-cart-ux.address-forms.billing-auto-match`)
- **Severity:** low
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-001106
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-001106)

## Why it matters

Most shoppers pay with a card registered to the same address they're shipping to. Making them type the exact same seven fields twice is pure friction with no payoff. A default-checked "Same as shipping" toggle collapses the billing section to a single checkbox for the common case while preserving separate-address entry for the minority that needs it. Missing this pattern adds roughly thirty seconds of typing and a fresh set of typo opportunities on every checkout.

## Severity rationale

Low because the defect only affects shoppers with matching addresses, but it adds friction on the majority path.

## Remediation

Add a default-checked "Same as shipping" toggle in `src/components/CheckoutAddresses.tsx`. When checked, copy shipping values into billing state and hide the billing fields. When unchecked, render the full billing form.

```tsx
const [sameAsShipping, setSameAsShipping] = useState(true)
<input type="checkbox" checked={sameAsShipping} onChange={e => { setSameAsShipping(e.target.checked); if (e.target.checked) setBillingAddress(shippingAddress) }} />
{!sameAsShipping && <BillingForm value={billingAddress} onChange={setBillingAddress} />}
```

## Detection

- **ID:** `billing-auto-match`
- **Severity:** `low`
- **What to look for:** Search checkout components for a "Same as shipping" or "Use shipping address for billing" checkbox/toggle. Count the billing address fields and check if they are conditionally rendered (hidden when the toggle is checked). Quote the toggle state variable and the conditional rendering logic. Verify the toggle defaults to checked (same as shipping) to minimize friction.
- **Pass criteria:** A "Same as shipping" checkbox or toggle is present, is checked by default, and hides at least 4 billing address fields when active. Users can uncheck to enter a separate billing address. Report: "Billing-match toggle found in [component], defaults to checked, hides X billing fields."
- **Fail criteria:** Users must manually enter billing address in full even if it matches shipping, or no copy/match mechanism exists.
- **Skip (N/A) when:** Only shipping address is collected (payment provider handles billing separately, or no separate billing address form exists).
- **Detail on fail:** Example: `"Billing address form at src/components/BillingForm.tsx always renders 5 fields. No 'Same as shipping' toggle found. Users must re-enter full address even when billing matches shipping."`
- **Cross-reference:** For address field count minimization, see the minimal-fields check above. For shipping address autocomplete, see the autocomplete check above.
- **Remediation:** Add a billing-match toggle in your checkout address section at `src/components/CheckoutAddresses.tsx`:

  ```tsx
  // src/components/CheckoutAddresses.tsx
  function CheckoutAddresses() {
    const [sameAsShipping, setSameAsShipping] = useState(true)
    const [shippingAddress, setShippingAddress] = useState({})
    const [billingAddress, setBillingAddress] = useState({})

    return (
      <>
        <ShippingForm value={shippingAddress} onChange={setShippingAddress} />
        <label>
          <input
            type="checkbox"
            checked={sameAsShipping}
            onChange={(e) => {
              setSameAsShipping(e.target.checked)
              if (e.target.checked) setBillingAddress(shippingAddress)
            }}
          />
          Billing address same as shipping
        </label>
        {!sameAsShipping && (
          <BillingForm value={billingAddress} onChange={setBillingAddress} />
        )}
      </>
    )
  }
  ```

Taxons: user-experience

HTML version: https://auditbuffet.com/patterns/ab-001106
