Skip to content

VueStripeAddressElement

A complete address collection form with built-in autocomplete powered by Google Maps.

No Backend Required

StripeAddressElement works without a clientSecret. It only needs to be wrapped in VueStripeProvider and VueStripeElements to function.

What is Address Element?

Address Element provides a complete, validated address collection form:

CapabilityDescription
Google Maps AutocompleteSuggests addresses as the user types
Address ValidationValidates addresses against postal databases
International SupportWorks with addresses from 200+ countries
Phone CollectionOptional phone number field with country code
Shipping/Billing ModesConfigurable for different use cases

How It Works

┌─────────────────────────────────────────────────────────────┐
│  AddressElement mounts inside StripeElements                │
│  (No clientSecret required - works standalone)              │
└─────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────┐
│  Renders address form based on mode (shipping/billing)      │
│  - Name field                                               │
│  - Address line 1 (with autocomplete suggestions)           │
│  - Address line 2                                           │
│  - City, State/Province, Postal Code                        │
│  - Country selector                                         │
│  - Phone (if configured)                                    │
└─────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────┐
│  User starts typing address                                 │
│  Google Maps suggests matching addresses                    │
└─────────────────────────────────────────────────────────────┘

              ┌───────────────┴───────────────┐
              ▼                               ▼
┌─────────────────────────┐     ┌─────────────────────────────┐
│  User selects           │     │  User types manually        │
│  suggestion             │     │                             │
│                         │     │  Form validates each field  │
│  Auto-fills all fields  │     │  as they complete it        │
└─────────────────────────┘     └─────────────────────────────┘


┌─────────────────────────────────────────────────────────────┐
│  Emits @change with { complete, isNewAddress, value }       │
│  complete: true when all required fields are valid          │
└─────────────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────┐
│  On submit: Call getValue() to get validated address        │
│  const { complete, value } = await addressRef.getValue()    │
└─────────────────────────────────────────────────────────────┘

Usage

vue
<template>
  <VueStripeProvider :publishable-key="publishableKey">
    <VueStripeElements>
      <VueStripeAddressElement
        :options="addressOptions"
        @ready="onReady"
        @change="onChange"
      />
      <button @click="handleSubmit">Continue</button>
    </VueStripeElements>
  </VueStripeProvider>
</template>

<script setup>
import {
  StripeProvider,
  StripeElements,
  StripeAddressElement
} from '@vue-stripe/vue-stripe'

const publishableKey = import.meta.env.VITE_STRIPE_PUBLISHABLE_KEY

const addressOptions = {
  mode: 'shipping',
  autocomplete: { mode: 'automatic' }
}

const onReady = () => {
  console.log('Address element ready')
}

const onChange = (event) => {
  if (event.complete) {
    console.log('Address complete:', event.value)
  }
}
</script>

Props

PropTypeRequiredDescription
optionsStripeAddressElementOptionsNoAddress element configuration (defaults to { mode: 'shipping' })

Options Object

ts
interface StripeAddressElementOptions {
  mode: 'shipping' | 'billing'
  autocomplete?: {
    mode: 'automatic' | 'disabled'
  }
  allowedCountries?: string[]
  blockPoBox?: boolean
  contacts?: Array<{
    name: string
    address: AddressValue
  }>
  defaultValues?: {
    name?: string
    firstName?: string
    lastName?: string
    phone?: string
    address?: {
      line1?: string
      line2?: string
      city?: string
      state?: string
      postal_code?: string
      country?: string
    }
  }
  display?: {
    name?: 'full' | 'split' | 'organization'
  }
  fields?: {
    phone?: 'always' | 'never' | 'auto'
  }
  validation?: {
    phone?: {
      required?: 'always' | 'never' | 'auto'
    }
  }
}

Events

EventPayloadDescription
@ready-Emitted when the element is fully rendered
@changeStripeAddressElementChangeEventEmitted when the address value changes
@focus-Emitted when any field gains focus
@blur-Emitted when any field loses focus
@escape-Emitted when the escape key is pressed
@load-error{ elementType: 'address', error: string }Emitted if the element fails to load

Change Event

ts
interface StripeAddressElementChangeEvent {
  elementType: 'address'
  complete: boolean
  isNewAddress: boolean
  value: {
    name?: string
    firstName?: string
    lastName?: string
    phone?: string
    address: {
      line1: string
      line2: string | null
      city: string
      state: string
      postal_code: string
      country: string
    }
  }
}

Exposed Methods

Access these methods via template ref:

vue
<script setup>
import { ref } from 'vue'

const addressRef = ref()

// Get current value programmatically
const validateAddress = async () => {
  const result = await addressRef.value?.getValue()
  if (result.complete) {
    console.log('Valid address:', result.value)
  } else {
    console.log('Address incomplete')
  }
}

// Focus the first field
const focusAddress = () => addressRef.value?.focus()

// Clear all fields
const clearAddress = () => addressRef.value?.clear()
</script>

<template>
  <VueStripeAddressElement ref="addressRef" />
  <button @click="validateAddress">Validate</button>
  <button @click="focusAddress">Focus</button>
  <button @click="clearAddress">Clear</button>
</template>
MethodReturnsDescription
getValue()Promise<{ complete, isNewAddress, value }>Get the current address data programmatically
focus()voidFocus the first input field
clear()voidClear all address fields

Exposed Properties

PropertyTypeDescription
elementRef<VueStripeAddressElement | null>The Stripe address element instance

Examples

Shipping vs Billing Mode

vue
<script setup>
import { ref } from 'vue'

const mode = ref('shipping')

const options = computed(() => ({
  mode: mode.value
}))
</script>

<template>
  <div>
    <button @click="mode = 'shipping'">Shipping</button>
    <button @click="mode = 'billing'">Billing</button>
  </div>
  <VueStripeAddressElement :key="mode" :options="options" />
</template>

With Default Values (Pre-fill)

vue
<script setup>
const options = {
  mode: 'shipping',
  defaultValues: {
    name: 'John Doe',
    phone: '+1 555-123-4567',
    address: {
      line1: '123 Main Street',
      line2: 'Apt 4B',
      city: 'San Francisco',
      state: 'CA',
      postal_code: '94102',
      country: 'US'
    }
  }
}
</script>

<template>
  <VueStripeAddressElement :options="options" />
</template>

With Phone Field

vue
<script setup>
const options = {
  mode: 'shipping',
  fields: {
    phone: 'always'
  },
  validation: {
    phone: {
      required: 'always'
    }
  }
}
</script>

<template>
  <VueStripeAddressElement :options="options" />
</template>

Limit Countries

vue
<script setup>
const options = {
  mode: 'shipping',
  allowedCountries: ['US', 'CA', 'GB', 'AU']
}
</script>

<template>
  <VueStripeAddressElement :options="options" />
</template>

Block P.O. Boxes

vue
<script setup>
const options = {
  mode: 'shipping',
  blockPoBox: true
}
</script>

<template>
  <VueStripeAddressElement :options="options" />
</template>

Using getValue() for Validation

vue
<script setup>
import { ref } from 'vue'

const addressRef = ref()
const isValid = ref(false)
const addressData = ref(null)

const handleSubmit = async () => {
  if (!addressRef.value) return

  const result = await addressRef.value.getValue()

  if (result.complete) {
    isValid.value = true
    addressData.value = result.value
    // Proceed with checkout
    console.log('Shipping to:', addressData.value.address)
  } else {
    isValid.value = false
    alert('Please complete the address form')
  }
}
</script>

<template>
  <VueStripeAddressElement ref="addressRef" :options="{ mode: 'shipping' }" />
  <button @click="handleSubmit">Continue to Payment</button>

  <div v-if="addressData">
    <h4>Confirmed Address:</h4>
    <p>{{ addressData.name }}</p>
    <p>{{ addressData.address.line1 }}</p>
    <p>{{ addressData.address.city }}, {{ addressData.address.state }} {{ addressData.address.postal_code }}</p>
  </div>
</template>

Integration with Payment Element

vue
<script setup>
import { ref } from 'vue'
import {
  StripeProvider,
  StripeElements,
  StripeAddressElement,
  StripePaymentElement,
  useStripe,
  useStripeElements
} from '@vue-stripe/vue-stripe'

const addressRef = ref()
const step = ref('address')

const { stripe } = useStripe()
const { elements } = useStripeElements()

const handleContinue = async () => {
  const result = await addressRef.value?.getValue()
  if (result?.complete) {
    step.value = 'payment'
  }
}

const handlePayment = async (clientSecret) => {
  const addressResult = await addressRef.value?.getValue()

  const { error } = await stripe.value.confirmPayment({
    elements: elements.value,
    confirmParams: {
      return_url: window.location.origin + '/success',
      shipping: {
        name: addressResult.value.name,
        address: addressResult.value.address
      }
    }
  })

  if (error) {
    console.error(error)
  }
}
</script>

<template>
  <VueStripeProvider :publishable-key="publishableKey">
    <VueStripeElements :options="{ clientSecret }">
      <!-- Step 1: Address -->
      <div v-if="step === 'address'">
        <h3>Shipping Address</h3>
        <VueStripeAddressElement ref="addressRef" :options="{ mode: 'shipping' }" />
        <button @click="handleContinue">Continue to Payment</button>
      </div>

      <!-- Step 2: Payment -->
      <div v-if="step === 'payment'">
        <h3>Payment</h3>
        <VueStripePaymentElement />
        <button @click="handlePayment(clientSecret)">Pay Now</button>
        <button @click="step = 'address'">Back</button>
      </div>
    </VueStripeElements>
  </VueStripeProvider>
</template>

TypeScript

ts
import { ref } from 'vue'
import { StripeAddressElement } from '@vue-stripe/vue-stripe'
import type {
  StripeAddressElement as StripeAddressElementType,
  StripeAddressElementChangeEvent,
  StripeAddressElementOptions
} from '@stripe/stripe-js'

// Options
const options: StripeAddressElementOptions = {
  mode: 'shipping',
  autocomplete: { mode: 'automatic' },
  fields: { phone: 'always' }
}

// Event handlers
const handleReady = () => {
  console.log('Address element ready')
}

const handleChange = (event: StripeAddressElementChangeEvent) => {
  console.log('Complete:', event.complete)
  console.log('Address:', event.value.address)
}

// Template ref with getValue()
const addressRef = ref<InstanceType<typeof StripeAddressElement>>()

const validateAddress = async () => {
  const result = await addressRef.value?.getValue()
  if (result?.complete) {
    // Type-safe address access
    const { line1, city, state, postal_code, country } = result.value.address
    console.log(`${line1}, ${city}, ${state} ${postal_code}, ${country}`)
  }
}

Key Features

  • Google Maps Autocomplete: Built-in address suggestions as you type
  • No clientSecret Required: Works immediately with just StripeElements
  • International Support: Handles addresses from all countries with proper formatting
  • Phone Number Support: Optional phone field with country-aware formatting
  • Validation: Built-in validation for complete addresses
  • Pre-fill Support: Populate with existing customer data
  • P.O. Box Blocking: Option to prevent P.O. Box addresses for physical delivery

Use Cases

  1. Checkout shipping address - Collect where to ship physical goods
  2. Billing address - Collect for payment verification
  3. Customer profile - Store addresses for future use
  4. Multi-step checkout - Collect address before payment details

See Also

Released under the MIT License.