> ## 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.1).
  The API is subject to change before the stable release.
</Warning>

`CardFormDefaults` is the namespace that holds the default content for [`PrimerCardForm`](/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
```

<Info>
  `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.
</Info>

## 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`](/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.
</Note>

### `unavailable()`

`PrimerCardForm` shows this placeholder when no checkout session is present in the environment — that is, when the `.primerCheckoutSession(_: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. Disabled when the form is invalid or a submission is in flight.                                                                        |

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

## 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(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.
</Tip>

## 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`. See [`PrimerCardFormSession`](/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>
  The per-field building blocks suppress the built-in co-badged network selector, so when you recompose individual card fields you must add `CardFormDefaults.cardNetwork(_:)` yourself if you support co-badged cards. Do not combine `cardNetwork(_:)` with the full `cardDetails(_:)` section — the section already renders the selector when multiple networks are detected, and you would see two selectors.
</Warning>

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

## See also

<CardGroup cols={2}>
  <Card title="PrimerCardForm" icon="credit-card" href="/docs/sdk/ios-checkout/v3.0.0-beta/api/card-form/primer-card-form">
    The card form view and its three section slots.
  </Card>

  <Card title="Card field components" icon="grip-lines" href="/docs/sdk/ios-checkout/v3.0.0-beta/api/card-form/card-field-components">
    Per-field building blocks for fully custom layouts.
  </Card>

  <Card title="PrimerCardFormSession" icon="signal" href="/docs/sdk/ios-checkout/v3.0.0-beta/api/card-form/card-form-session">
    The observable session that drives form state and submission.
  </Card>
</CardGroup>
