> ## Documentation Index
> Fetch the complete documentation index at: https://docs.whop.com/llms.txt
> Use this file to discover all available pages before exploring further.

# AddressElement

> Collects a billing or shipping address using the format of the selected country: which fields appear, the order they appear in, which of them are required and how the postal code is validated all follow that country. The street field offers address suggestions as the buyer types, and the address can be read and validated at any point.

<Info>**Upcoming.** These docs cover unreleased development, ahead of any published release. Use the channel picker at the top of the sidebar for the docs of a published release.</Info>

*In development, not yet part of a stable release.*

<Tabs>
  <Tab title="Web">
    Belongs to the [`Payments`](/elements/upcoming/payments/overview) group. Render `<AddressElement />` inside it (React), or call `payments.create('address', { … })` on the handle (vanilla). Consumer props and `on<Event>` callbacks both go in the create options / JSX props.

    ## Preview

    A live, interactive demo of this element with sample data:

    <div data-whop-demo-shell style={{ position: "relative", minHeight: "320px", transition: "min-height 200ms ease" }}>
      <div data-whop-demo-skeleton style={{ position: "absolute", inset: "0", borderRadius: "12px", background: "rgba(140, 140, 140, 0.12)", pointerEvents: "none", transition: "opacity 200ms ease" }} />

      <div data-whop-demo-native="element:payments/address" data-whop-elements-version="" style={{ position: "relative" }} />
    </div>

    ## Usage

    <div data-whop-usage="payments/address">
      <CodeGroup>
        ```tsx React theme={null}
        import { WhopElements, Payments, AddressElement } from "@whop/elements-react";
        import { loadWhop } from "@whop/elements";

        function Example() {
          return (
            <WhopElements elements={loadWhop()}>
              <Payments /* options */>
                <AddressElement onChange={(payload) => console.log("change", payload)} />
              </Payments>
            </WhopElements>
          );
        }
        ```

        ```html Vanilla theme={null}
        <script src="https://js.whop.cloud/elements/amber/elements.js" data-whop-elements></script>
        <script type="module">
          const payments = window.WhopElements().payments.create({ /* options */ });
          payments.create('address', { onChange: (payload) => console.log("change", payload) }).mount('#payments-address');
        </script>
        ```
      </CodeGroup>
    </div>

    ## Props

    <ResponseField name="layout" type="&#x22;full&#x22; | &#x22;compact&#x22;">
      `full` (default) stacks labeled fields; `compact` fuses them into one dense bordered group with placeholder labels. Defaults to `"full"`.
    </ResponseField>

    <ResponseField name="line2" type="&#x22;never&#x22; | &#x22;toggle&#x22; | &#x22;always&#x22;">
      Address line 2: always visible (default), revealed by a text button (`toggle`), or never collected. Defaults to `"always"`.
    </ResponseField>

    <ResponseField name="name" type="&#x22;split&#x22; | &#x22;combined&#x22; | &#x22;none&#x22;">
      The name row: one full-name field (default), split first/last fields, or none. Defaults to `"combined"`.
    </ResponseField>

    <ResponseField name="mode" type="&#x22;billing&#x22; | &#x22;shipping&#x22;">
      Which autocomplete family the fields advertise to the browser: `billing` (default) or `shipping`. Defaults to `"billing"`.
    </ResponseField>

    <ResponseField name="scope" type="&#x22;full&#x22; | &#x22;minimal&#x22;">
      `full` (default) collects the country's complete address format; `minimal` collects the AVS floor of ADDRESS fields only — country plus postal code. The name row is governed by the `name` prop independently of scope, and a rendered name row is required (set `name: 'none'` for a pure country+postal collection). Note: the standalone element never widens to a payment method's own declared billing floor — a card confirm requires the cardholder name, country, and postal code, so beside a card method keep `name` enabled or pass it in the confirm's `billingDetails`. Defaults to `"full"`.
    </ResponseField>

    <ResponseField name="organization" type="&#x22;name&#x22; | &#x22;none&#x22; | &#x22;name_with_type&#x22;">
      The organization row: hidden (default), a name field, or a name field plus a business/individual type select. Defaults to `"none"`.
    </ResponseField>

    <ResponseField name="defaultValues" type="{ name?: string | undefined; address?: { name?: string | undefined; first_name?: string | undefined; last_name?: string | undefined; organization?: string | undefined; organization_type?: &#x22;business&#x22; | &#x22;individual&#x22; | undefined; line1?: string | undefined; line2?: string | undefined; city?: string | undefined; state?: string | undefined; postal_code?: string | undefined; country?: string | undefined; } | undefined; }">
      Seed values applied once before first paint (`address.country` is ISO2). Takes precedence over IP-country detection.
    </ResponseField>

    <ResponseField name="detectCountry" type="boolean">
      Default the country from the buyer's IP (resolved before first paint). Falls back to the controller's `countryHint`, then US. Defaults to `true`.
    </ResponseField>

    <ResponseField name="allowedCountries" type="string[]">
      Restrict the country select to these ISO2 codes. A single entry locks the country — the select stays openable and shows the one choice.
    </ResponseField>

    <ResponseField name="autocomplete" type="boolean">
      Google Places suggestions on the street-address field. The suggestions box opens as the buyer types; when nothing matches (or Places is blocked) it shows a quiet "no matches" line with "Enter address manually" still available. Set false for a plain input. Defaults to `true`.
    </ResponseField>

    <ResponseField name="customFields" type="({ key: string; label: string; type: &#x22;text&#x22; | &#x22;select&#x22; | &#x22;date&#x22; | &#x22;phone&#x22;; position: &#x22;after_name&#x22; | &#x22;after_organization&#x22; | &#x22;before_country&#x22; | &#x22;after_address&#x22;; required?: boolean | undefined; options?: string[] | undefined; format?: string | undefined; autocomplete?: string | undefined; })[]">
      Extra fields rendered inline with the same chrome: `{ key, label, type: text|select|date|phone, position: after_name|after_organization|before_country|after_address, required?, options?, format?, autocomplete? }`. Values are validated centrally and emitted under the separate `custom` bag.
    </ResponseField>

    <ResponseField name="countryHint" type="string">
      Optional ISO2 fallback for the country-detect chain (defaultValues > IP country > this > US) — e.g. derived from a checkout currency when it maps to exactly one country. Empty (default) = no hint. (When the payments controller later declares this element, its currency-derived hint wants to layer over this — the framework has no controller-OR-consumer prop source yet, so that wiring lands with that chunk.) Defaults to `""`.
    </ResponseField>

    ## Events

    Pass a callback in the create options (or React prop) to receive these.

    ### `onChange`

    The address changed — `complete: true` means every required field for the selected country (plus any required custom fields) is filled and valid. `address` uses the confirmation-token billing\_details naming (`postal_code`, ISO2 `country`); custom field values ride the separate `custom` bag.

    **Signature:** `((payload: { complete: boolean; address: { name?: string | undefined; first_name?: string | undefined; last_name?: string | undefined; organization?: string | undefined; organization_type?: "business" | "individual" | undefined; line1?: string | undefined; line2?: string | undefined; city?: string | undefined; state?: string | undefined; postal_code?: string | undefined; country: string; }; custom: Record<string, string>; }) => void)`

    ### `onLoaderStart`

    Fired the moment the element's own loading skeleton has painted inside its frame. This is the earliest point a consumer-managed loading state can hand off without ever exposing a blank. Always precedes `onReady`; most consumers only need `onReady`.

    **Signature:** `(() => void)`

    ### `onReady`

    Fired once the element has booted and painted its first complete frame.

    **Signature:** `(() => void)`

    ### `onError`

    Fired when the element fails to load or crashes; the element shows its own error fallback. `message` is human-readable; framework refusals also carry `code` (e.g. `HOST_SOURCE_FAILED`, with `sourceKey` naming the failed hostState key) so hosts can switch on codes, never message text.

    **Signature:** `((e: { message: string; code?: string | undefined; sourceKey?: string | undefined; }) => void)`

    ## Methods

    Call these on the element handle, which is the return of `create` (vanilla) or the component `ref` (React).

    ### `validate`

    Validate the whole form, revealing every error inline. Resolves `{ complete, address, errors }` — never throws; `errors` is empty exactly when `complete` is true. Error keys are the contract field keys (`postal_code`, `line1`, …), `custom:<key>` for custom fields, and the transient `form` key returned while the form is still loading.

    **Signature:** `() => Promise<{ complete: boolean; address: { name?: string | undefined; first_name?: string | undefined; last_name?: string | undefined; organization?: string | undefined; organization_type?: "business" | "individual" | undefined; line1?: string | undefined; line2?: string | undefined; city?: string | undefined; state?: string | undefined; postal_code?: string | undefined; country: string; }; errors: Record<string, string>; }>`

    ### `getValues`

    Read the current values without validating or revealing errors: `{ address, custom }`.

    **Signature:** `() => Promise<{ address: { name?: string | undefined; first_name?: string | undefined; last_name?: string | undefined; organization?: string | undefined; organization_type?: "business" | "individual" | undefined; line1?: string | undefined; line2?: string | undefined; city?: string | undefined; state?: string | undefined; postal_code?: string | undefined; country: string; }; custom: Record<string, string>; }>`

    ### `mount`

    Place the element on the page: appends its container to `target` (a CSS selector or an element) and starts loading. Nothing renders until this is called. React consumers never call it; the component mounts itself.

    **Signature:** `(target: string | HTMLElement) => void`

    ### `destroy`

    Remove the element from the page and release its frame and subscriptions. Safe to call more than once. React consumers never call it; unmounting the component does it.

    **Signature:** `() => void`

    ### `update`

    Change this element's consumer props after mount, and it re-renders with the merged props. React consumers never call it; updating the JSX props does the same.

    **Signature:** `(options: Partial<AddressElementProps>) => void`

    ## Styling

    Each part below is a stable class name, safe to depend on. Restyle a part by mapping its class to a **style declaration object** under `appearance.classes` (properties camelCase or kebab-case, values as strings with units, the same shape as React's `style` prop). The element renders in its own frame, so page stylesheets can't reach it: these declarations are sanitized against a safe-property allowlist and injected inside the frame for you.

    | Class                            | Targets                                                                                             |
    | -------------------------------- | --------------------------------------------------------------------------------------------------- |
    | `.whop-Address`                  | The address form root                                                                               |
    | `.whop-AddressErrorSummary`      | The summary line shown when validation reveals missing or invalid fields                            |
    | `.whop-AddressField`             | One field cell in the address form                                                                  |
    | `.whop-AddressFieldError`        | The error line under an address field (full layout)                                                 |
    | `.whop-AddressFieldInput`        | A text input in the address form                                                                    |
    | `.whop-AddressFieldInputInvalid` | Added to an address input while it fails validation                                                 |
    | `.whop-AddressFieldInvalid`      | Added to a compact field cell while it fails validation                                             |
    | `.whop-AddressFieldLabel`        | The label above an address field (full layout)                                                      |
    | `.whop-AddressFieldSelect`       | A select (country, state, organization type) in the address form                                    |
    | `.whop-AddressLine2Toggle`       | The "Add address line 2" text button shown while line 2 is collapsed                                |
    | `.whop-AddressManualEntry`       | The "Enter address manually" text button below the collapsed form — expands the full country format |
    | `.whop-AddressSuggestion`        | One suggestion row in the autocomplete overlay                                                      |
    | `.whop-AddressSuggestionActive`  | Added to the keyboard/pointer-active suggestion row                                                 |
    | `.whop-AddressSuggestionManual`  | The "Enter address manually" row closing the suggestions list                                       |
    | `.whop-AddressSuggestions`       | The autocomplete suggestions overlay anchored to the address line 1 field                           |
    | `.whop-AddressSuggestionsEmpty`  | The quiet line shown when the query settled with no address matches                                 |

    ```ts theme={null}
    const payments = whop.payments.create({
      appearance: {
        classes: {
          'whop-Address': { borderRadius: '8px', fontWeight: '600' },
          'whop-AddressErrorSummary': { borderRadius: '8px', fontWeight: '600' },
          'whop-AddressField': { borderRadius: '8px', fontWeight: '600' },
          'whop-AddressFieldError': { borderRadius: '8px', fontWeight: '600' },
          'whop-AddressFieldInput': { borderRadius: '8px', fontWeight: '600' },
          'whop-AddressFieldInputInvalid': { borderRadius: '8px', fontWeight: '600' },
          'whop-AddressFieldInvalid': { borderRadius: '8px', fontWeight: '600' },
          'whop-AddressFieldLabel': { borderRadius: '8px', fontWeight: '600' },
          'whop-AddressFieldSelect': { borderRadius: '8px', fontWeight: '600' },
          'whop-AddressLine2Toggle': { borderRadius: '8px', fontWeight: '600' },
          'whop-AddressManualEntry': { borderRadius: '8px', fontWeight: '600' },
          'whop-AddressSuggestion': { borderRadius: '8px', fontWeight: '600' },
          'whop-AddressSuggestionActive': { borderRadius: '8px', fontWeight: '600' },
          'whop-AddressSuggestionManual': { borderRadius: '8px', fontWeight: '600' },
          'whop-AddressSuggestions': { borderRadius: '8px', fontWeight: '600' },
          'whop-AddressSuggestionsEmpty': { borderRadius: '8px', fontWeight: '600' }
        }
      }
    });

    // restyle live at any point, the same shape through update()
    payments.update({
      appearance: { classes: { 'whop-Address': { fontWeight: '700' } } }
    });
    ```

    In React, pass the same object as the `appearance` prop on `<Payments>`; `appearance` also applies globally at `WhopElements({ appearance })`.
  </Tab>

  <Tab title="Swift">
    <div style={{ display: "flex", gap: "2.5rem", alignItems: "flex-start", flexWrap: "wrap" }}>
      <div style={{ flex: "1 1 24rem", minWidth: 0 }}>
        `AddressElement` collects a billing or shipping address using the selected country's own format: which fields exist, the order they appear in, which of them are required and how the postal code is validated all follow that country, and the street field offers suggestions as the buyer types. Unlike the wallet views it reads nothing from Whop, so it needs no token and renders before (or without) `WhopSDK.configure`. Hold an `AddressElementManager` where your submit button lives to read and validate what it holds.

        ### Usage

        ```swift theme={null}
        import SwiftUI
        import WhopElements

        // Nothing here is read from Whop, so this screen needs no WhopSDK.configure.
        struct CheckoutScreen: View {
            @State private var manager = AddressElementManager()

            var body: some View {
                ScrollView {
                    AddressElement(manager: manager) { snapshot in
                        print(snapshot.isComplete)
                    }
                    .padding()
                }
                .safeAreaInset(edge: .bottom) {
                    Button("Continue") {
                        let snapshot = manager.validate()
                        guard snapshot.isComplete else { return }
                        print(snapshot.address.country, snapshot.address.postalCode ?? "")
                    }
                    .padding()
                }
            }
        }
        ```

        ### Parameters

        <ResponseField name="manager" type="AddressElementManager?">
          Reads and validates the address from outside the view. Hold one with `@State` where your submit button lives, then call `validate()` before submitting.
        </ResponseField>

        <ResponseField name="layout" type="AddressElement.Layout">
          `.full` labels every field and stacks them; `.compact` moves the labels into placeholders. Defaults to `.full`.
        </ResponseField>

        <ResponseField name="scope" type="AddressElement.Scope">
          `.full` collects the country's whole address format; `.minimal` collects country and postal code only. Defaults to `.full`.
        </ResponseField>

        <ResponseField name="name" type="AddressElement.NameFields">
          `.combined` for one full-name field, `.split` for first and last, `.none` to leave the name out. Defaults to `.combined`.
        </ResponseField>

        <ResponseField name="organization" type="AddressElement.OrganizationFields">
          `.none`, `.name` for an organization name, or `.nameWithType` to also ask whether it is a business or an individual. Defaults to `.none`.
        </ResponseField>

        <ResponseField name="line2" type="AddressElement.Line2Field">
          `.always` shows the second line, `.toggle` reveals it with a button, `.never` leaves it out. Defaults to `.always`.
        </ResponseField>

        <ResponseField name="defaultValues" type="WhopAddress?">
          Values to start from. Its `country` is an ISO 3166-1 alpha-2 code and takes precedence over country detection.
        </ResponseField>

        <ResponseField name="detectCountry" type="Bool">
          Start on the device's region. Defaults to `true`, and falls back to `countryHint` and then US.
        </ResponseField>

        <ResponseField name="allowedCountries" type="[String]?">
          Restrict the country picker to these ISO 3166-1 alpha-2 codes. Defaults to every country.
        </ResponseField>

        <ResponseField name="countryHint" type="String?">
          ISO 3166-1 alpha-2 fallback for the country chain, for example one derived from the buyer's currency.
        </ResponseField>

        <ResponseField name="autocomplete" type="Bool">
          Street suggestions as the buyer types. Defaults to `true`. Turning it off also shows every field from the start, since there is no suggestion to wait for.
        </ResponseField>

        <ResponseField name="onChange" type="((WhopAddressSnapshot) -> Void)?">
          Called on every edit with the current snapshot.
        </ResponseField>

        ### `WhopAddressSnapshot`

        What a selection hands back:

        * `isComplete: Bool`: every field the country requires is filled and valid
        * `address: WhopAddress`: what the buyer has entered so far
        * `errors: [WhopAddressField: WhopAddressFieldError]`: empty exactly when `isComplete` is true

        ### States

        There is nothing to fetch, so it renders immediately with no loading or error state of its own. `manager.validate()` reveals the per-field errors inline and returns the same snapshot `onChange` receives; `errors` is empty exactly when `isComplete` is true. While `autocomplete` is on at `.full` scope the locality fields stay hidden until the buyer picks a suggestion or opens the form manually — turn it off and every field is shown from the start.

        ### Good to know

        * Street suggestions come from MapKit on the device, so what the buyer types is not sent to Whop as they type.
        * `defaultValues.country` beats `detectCountry`; with neither, the country falls back to `countryHint` and then to US.
        * Field names on `WhopAddress` match the web element's payload and the confirmation token's `billing_details` keys, so the same JSON works on both platforms.

        <Note>
          Authentication is set once for every element, not per element, so `WhopSDK.configure(tokenProvider:)` at app launch is enough. A view mounted before it lands renders a spinner and picks the token up when it arrives. See [Getting started](/elements/upcoming/getting-started) for the token provider. Theme with the `.whopTheme(_:)` modifier.
        </Note>
      </div>

      <div style={{ flex: "0 1 22rem", width: "22rem", maxWidth: "100%" }}>
        <div style={{ position: "sticky", top: "5rem" }}>
          <iframe src="https://app.revyl.ai/embed/89c536ac-28ef-45d0-b99a-ecfffe579e33?accentColor=%23111" title="AddressElement running on an iPhone simulator" loading="lazy" className="whop-ios-simulator" style={{ width: "100%", aspectRatio: "390 / 800", border: 0, display: "block", borderRadius: "1.25rem" }} allow="fullscreen; clipboard-read; clipboard-write" />
        </div>
      </div>
    </div>
  </Tab>
</Tabs>
