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

# CardFormDefaults

> Pre-built section slot bodies for custom card form layouts

> [!WARNING]
>
> Primer Checkout iOS SDK is currently in **beta** (v3.0.0-beta.8).
> The API is subject to change before the stable release.

`CardFormDefaults` is the namespace that holds the default content for [`PrimerCardForm`](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/card-form/primer-card-form)'s slots. When you create a card form without supplying a slot, `PrimerCardForm` falls back to the matching `CardFormDefaults` helper. You can also call these helpers yourself inside a custom slot to keep the default rendering while wrapping it with your own layout.

```swift theme={"dark"}
@available(iOS 15.0, *)
public enum CardFormDefaults
```

> [!NOTE]
>
> `CardFormDefaults` is an empty enum used purely as a namespace for static factory methods. You never instantiate it; you call its `static func` members and embed the views they return.

## How the defaults are wired

`PrimerCardForm` declares each `@ViewBuilder` slot with a default argument that points at the matching `CardFormDefaults` helper:

```swift theme={"dark"}
public init(
  @ViewBuilder cardDetails: @escaping (PrimerCardFormSession) -> CardDetails = { CardFormDefaults.cardDetails($0) },
  @ViewBuilder billingAddress: @escaping (PrimerCardFormSession) -> Billing = { CardFormDefaults.billingAddress($0) },
  @ViewBuilder submitButton: @escaping (PrimerCardFormSession) -> Submit = { CardFormDefaults.submitButton($0) }
)
```

Each helper receives the [`PrimerCardFormSession`](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/card-form/card-form-session) for the current checkout and returns a concrete, config-aware view. Because they return concrete view types, they can satisfy `PrimerCardForm`'s generic view parameters (`CardDetails`, `Billing`, `Submit`) at the default-value site, where an opaque `some View` cannot.

## Section helpers

These three helpers produce the bodies of `PrimerCardForm`'s three slots.

| Method | Returns | Description |
| - | - | - |
| `cardDetails(_:)` | `CardDetailsContent` | Default card-details section (number, expiry, CVV, cardholder name). |
| `billingAddress(_:)` | `BillingAddressContent` | Default billing-address section. Renders only when the configuration requires billing fields. |
| `submitButton(_:)` | `CardSubmitButton` | Default submit button, enabled when the form is valid and not loading. |

```swift theme={"dark"}
public static func cardDetails(_ session: PrimerCardFormSession) -> CardDetailsContent

public static func billingAddress(_ session: PrimerCardFormSession) -> BillingAddressContent

public static func submitButton(_ session: PrimerCardFormSession) -> CardSubmitButton
```

> [!NOTE]
>
> `billingAddress(_:)` and the individual billing fields render nothing unless the API-driven form configuration includes billing fields, so it is always safe to include the section. The decision is made for you from the session's configuration.

### `unavailable()`

`PrimerCardForm` shows this placeholder when no checkout session is present in the environment — that is, when the `.primerCheckoutSession(_:theme:onCompletion:)` modifier has not been applied, or the session is not yet ready.

```swift theme={"dark"}
public static func unavailable() -> some View
```

It returns an `EmptyView`, so an unconfigured form occupies no space rather than crashing or showing partial UI.

## Returned section views

The section helpers return concrete `View` types. You rarely name these directly — you embed them inside your own layout — but they are public so you can store and compose them.

| Type | Description |
| - | - |
| `CardDetailsContent` | The shared, config-aware card-details renderer (number, expiry, CVV, cardholder name, and the co-badged network selector when multiple networks are detected). |
| `BillingAddressContent` | The shared, config-aware billing-address renderer. Renders only the fields the configuration requires. |
| `CardSubmitButton` | The default pay button, labeled with the localized "Pay" title. Disabled when the form is invalid or a payment is loading. |

All three are annotated `@available(iOS 15.0, *)` and conform to `View`.

> [!NOTE]
>
> While a payment is loading, VoiceOver reads the value "Processing payment, please wait" on the default `CardSubmitButton`. When the form is invalid and no payment is loading, VoiceOver reads the hint "Button disabled. Complete all required fields to enable payment". The SDK localizes both strings.

## Wrapping the default in a custom slot

The most common reason to call `CardFormDefaults` directly is to keep the SDK's rendering while adding your own chrome around it. Pass a slot closure to `PrimerCardForm`, then call the matching helper inside it.

```swift theme={"dark"}
import SwiftUI
import PrimerSDK

struct CheckoutView: View {
  @StateObject private var session: PrimerCheckoutSession

  init(clientToken: String) {
    _session = StateObject(wrappedValue: PrimerCheckoutSession(clientToken: clientToken))
  }

  var body: some View {
    ScrollView {
      PrimerCardForm(
        cardDetails: { formSession in
          VStack(alignment: .leading, spacing: 16) {
            Text("Card details")
              .font(.headline)

            // Keep the default fields, wrapped in your own layout.
            CardFormDefaults.cardDetails(formSession)

            Text("Your card is encrypted and never stored on this device.")
              .font(.footnote)
              .foregroundColor(.secondary)
          }
          .padding()
        }
      )
    }
    .primerCheckoutSession(session) { state in
      handle(state)
    }
  }
}
```

> [!TIP]
>
> Because each helper returns a concrete view, you can place it anywhere a SwiftUI view is expected — inside a `VStack`, a `GroupBox`, a card-styled container, or alongside your own promotional content.

## Replacing only the submit button

To keep the default fields but swap the pay button, supply only the `submitButton` slot and leave the other two at their defaults:

```swift theme={"dark"}
PrimerCardForm(
  submitButton: { formSession in
    Button {
      formSession.submit()
    } label: {
      Text("Pay now")
        .frame(maxWidth: .infinity)
    }
    .buttonStyle(.borderedProminent)
    .disabled(!formSession.state.isValid || formSession.state.isLoading)
  }
)
```

Read `formSession.state.isValid` and `formSession.state.isLoading` to mirror the enablement logic of the default `CardSubmitButton`. A custom button does not get the default button's VoiceOver value and hint, so set your own accessibility value and hint from the same two flags. See [`PrimerCardFormSession`](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/card-form/card-form-session) for the full state surface.

## Recomposing individual fields

The section helpers are coarse-grained: they render an entire section. For fine-grained layouts, `CardFormDefaults` also exposes per-field building blocks (`cardNumber(_:)`, `cvv(_:)`, `firstName(_:)`, and so on) on the same namespace. Compose those inside your `cardDetails` or `billingAddress` slot to arrange fields exactly how you want.

```swift theme={"dark"}
PrimerCardForm(
  cardDetails: { formSession in
    VStack(spacing: 12) {
      CardFormDefaults.cardNumber(formSession)
      HStack(spacing: 12) {
        CardFormDefaults.expiryDate(formSession)
        CardFormDefaults.cvv(formSession)
      }
      CardFormDefaults.cardholderName(formSession)
    }
  }
)
```

> [!WARNING]
>
> When more than one network is detected, `CardFormDefaults.cardNumber(_:)` shows the co-badged network selector inside the card number field. The full `cardDetails(_:)` section does the same. When the shopper cannot choose between the detected networks, the field shows a badge for each detected network instead. Do not add `CardFormDefaults.cardNetwork(_:)` next to `cardNumber(_:)` or `cardDetails(_:)`, or the shopper sees two selectors.

The per-field building blocks are documented in full on [Card field components](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/card-form/card-field-components.md).

## See also

> [card] [PrimerCardForm](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/card-form/primer-card-form.md)
>
> The card form view and its three section slots.

> [card] [Card field components](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/card-form/card-field-components.md)
>
> Per-field building blocks for fully custom layouts.

> [card] [PrimerCardFormSession](https://primer.io/docs/sdk/ios-checkout/v3.0.0-beta/api/card-form/card-form-session.md)
>
> The observable session that drives form state and submission.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.