> ## 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.

# Components

> Overview of all Primer Checkout components and their relationships.

export const IOS_CHECKOUT_HIERARCHY = {
  name: 'PrimerCheckout',
  type: 'root',
  badge: 'managed SwiftUI View',
  note: 'or PrimerCheckoutSession via .primerCheckoutSession(_:onCompletion:)',
  children: [{
    name: 'PrimerPaymentMethods',
    type: 'container',
    badge: 'available methods',
    children: [{
      name: 'header',
      type: 'utility',
      note: 'slot',
      inline: true
    }, {
      name: 'method',
      type: 'input',
      note: 'slot × N',
      inline: true
    }, {
      name: 'emptyState',
      type: 'utility',
      note: 'slot',
      inline: true
    }]
  }, {
    name: 'PrimerCardForm',
    type: 'form',
    badge: 'card payment form',
    children: [{
      name: 'cardDetails',
      type: 'input',
      note: 'slot',
      fullWidth: [{
        name: 'CardFormDefaults.cardNumber',
        type: 'input'
      }],
      grid: [{
        name: 'CardFormDefaults.expiryDate',
        type: 'input'
      }, {
        name: 'CardFormDefaults.cvv',
        type: 'input'
      }],
      bottomFullWidth: [{
        name: 'CardFormDefaults.cardholderName',
        type: 'input'
      }, {
        name: 'CardFormDefaults.cardNetwork',
        type: 'input'
      }]
    }, {
      name: 'billingAddress',
      type: 'input',
      note: 'slot',
      inline: true
    }, {
      name: 'submitButton',
      type: 'action',
      note: 'slot',
      inline: true
    }]
  }, {
    name: 'PrimerVaultedPaymentMethods',
    type: 'utility',
    badge: 'saved methods',
    children: [{
      name: 'header',
      type: 'utility',
      note: 'slot',
      inline: true
    }, {
      name: 'item',
      type: 'input',
      note: 'slot × N',
      inline: true
    }, {
      name: 'submitButton',
      type: 'action',
      note: 'slot',
      inline: true
    }]
  }]
};

export const ANDROID_CHECKOUT_HIERARCHY_HOST = {
  name: 'rememberPrimerCheckoutController()',
  type: 'root',
  badge: 'controller',
  children: [{
    name: 'PrimerCheckoutHost',
    type: 'layout',
    badge: 'inline host',
    children: [{
      name: 'Your layout with SDK components',
      type: 'container',
      note: 'composable lambda',
      children: [{
        name: 'PrimerPaymentMethods',
        type: 'container',
        inline: true
      }, {
        name: 'PrimerCardForm',
        type: 'form',
        inline: true
      }, {
        name: 'PrimerVaultedPaymentMethods',
        type: 'utility',
        inline: true
      }]
    }]
  }]
};

export const ANDROID_CHECKOUT_HIERARCHY_SHEET = {
  name: 'rememberPrimerCheckoutController()',
  type: 'root',
  badge: 'controller',
  children: [{
    name: 'PrimerCheckoutSheet',
    type: 'layout',
    badge: 'modal bottom sheet',
    children: [{
      name: 'PrimerCheckoutSheetDefaults.Splash',
      type: 'utility',
      inline: true
    }, {
      name: 'PrimerCheckoutSheetDefaults.Loading',
      type: 'utility',
      inline: true
    }, {
      name: 'PrimerCheckoutSheetDefaults.PaymentMethodSelection',
      type: 'container',
      children: [{
        name: 'PrimerVaultedPaymentMethods',
        type: 'utility',
        note: 'saved methods',
        inline: true
      }, {
        name: 'PrimerPaymentMethods',
        type: 'container',
        note: 'available methods',
        inline: true
      }]
    }, {
      name: 'PrimerCardForm',
      type: 'form',
      badge: 'card payment form',
      fullWidth: [{
        name: 'CardNumberField',
        type: 'input'
      }],
      grid: [{
        name: 'ExpiryField',
        type: 'input'
      }, {
        name: 'CvvField',
        type: 'input'
      }],
      bottomFullWidth: [{
        name: 'CardholderField',
        type: 'input'
      }, {
        name: 'SubmitButton',
        type: 'action'
      }]
    }, {
      name: 'PrimerCheckoutSheetDefaults.Success',
      type: 'utility',
      inline: true
    }, {
      name: 'PrimerCheckoutSheetDefaults.Error',
      type: 'error',
      inline: true
    }]
  }]
};

export const CHECKOUT_HIERARCHY_WITH_VAULT = {
  name: '<primer-checkout>',
  type: 'root',
  badge: 'root',
  children: [{
    name: '<primer-main>',
    type: 'layout',
    badge: 'slot: main',
    children: [{
      name: '<primer-vault-manager>',
      type: 'utility',
      inline: true
    }, {
      name: '<primer-show-other-payments>',
      type: 'container',
      children: [{
        name: '<primer-payment-method>',
        type: 'container',
        note: '× N',
        inline: true
      }, {
        name: '<primer-card-form>',
        type: 'form',
        badge: 'slot: card-form-content',
        fullWidth: [{
          name: 'primer-input-card-number',
          type: 'input'
        }],
        grid: [{
          name: 'primer-input-card-expiry',
          type: 'input'
        }, {
          name: 'primer-input-cvv',
          type: 'input'
        }],
        bottomFullWidth: [{
          name: 'primer-input-card-holder-name',
          type: 'input'
        }, {
          name: 'primer-card-form-submit',
          type: 'action'
        }]
      }]
    }, {
      name: '<primer-error-message-container>',
      type: 'error',
      inline: true
    }]
  }]
};

export const CHECKOUT_HIERARCHY_WITHOUT_VAULT = {
  name: '<primer-checkout>',
  type: 'root',
  badge: 'root',
  children: [{
    name: '<primer-main>',
    type: 'layout',
    badge: 'slot: main',
    children: [{
      name: '<primer-payment-method>',
      type: 'container',
      note: '× N',
      inline: true
    }, {
      name: '<primer-card-form>',
      type: 'form',
      badge: 'slot: card-form-content',
      fullWidth: [{
        name: 'primer-input-card-number',
        type: 'input'
      }],
      grid: [{
        name: 'primer-input-card-expiry',
        type: 'input'
      }, {
        name: 'primer-input-cvv',
        type: 'input'
      }],
      bottomFullWidth: [{
        name: 'primer-input-card-holder-name',
        type: 'input'
      }, {
        name: 'primer-card-form-submit',
        type: 'action'
      }]
    }, {
      name: '<primer-error-message-container>',
      type: 'error',
      inline: true
    }]
  }]
};

export const ComponentHierarchyBox = ({data}) => {
  const colors = {
    root: {
      border: '#3b82f6',
      text: '#3b82f6'
    },
    layout: {
      border: '#8b5cf6',
      text: '#8b5cf6'
    },
    container: {
      border: '#6b7280',
      text: '#6b7280'
    },
    form: {
      border: '#10b981',
      text: '#10b981'
    },
    input: {
      border: '#f59e0b',
      text: '#f59e0b'
    },
    action: {
      border: '#ef4444',
      text: '#ef4444'
    },
    error: {
      border: '#ef4444',
      text: '#ef4444'
    },
    utility: {
      border: '#6b7280',
      text: '#6b7280'
    }
  };
  const renderNode = (node, depth = 0) => {
    const color = colors[node.type] || colors.container;
    const isLeaf = !node.children || node.children.length === 0;
    const borderStyle = node.conditional ? 'dashed' : 'solid';
    return <div key={node.name} style={{
      border: '1px ' + borderStyle + ' ' + color.border,
      borderRadius: '8px',
      padding: isLeaf ? '10px 14px' : '16px',
      marginTop: depth > 0 ? '12px' : '0'
    }}>
        <div style={{
      display: 'flex',
      alignItems: 'center',
      gap: '12px',
      marginBottom: node.children ? '12px' : '0',
      flexWrap: 'wrap'
    }}>
          <span style={{
      fontFamily: 'var(--font-mono, monospace)',
      fontSize: '14px',
      fontWeight: '500',
      color: color.text
    }}>
            {node.name}
          </span>
          {node.badge && <span style={{
      padding: '2px 8px',
      fontSize: '11px',
      fontWeight: '500',
      color: 'var(--tw-prose-body, #6b7280)',
      border: '1px solid var(--tw-prose-hr, #e5e7eb)',
      borderRadius: '4px'
    }}>
              {node.badge}
            </span>}
          {node.note && <span style={{
      fontSize: '12px',
      color: 'var(--tw-prose-body, #9ca3af)'
    }}>
              {node.note}
            </span>}
        </div>

        {node.children && <div style={{
      display: 'flex',
      flexDirection: 'column',
      gap: '0'
    }}>
            {node.children.map(child => {
      if (child.inline) {
        const childBorderStyle = child.conditional ? 'dashed' : 'solid';
        return <div key={child.name} style={{
          display: 'flex',
          alignItems: 'center',
          gap: '8px',
          padding: '8px 12px',
          border: '1px ' + childBorderStyle + ' ' + (colors[child.type] || colors.container).border,
          borderRadius: '6px',
          marginTop: '8px'
        }}>
                    <span style={{
          fontFamily: 'var(--font-mono, monospace)',
          fontSize: '13px',
          color: (colors[child.type] || colors.container).text
        }}>
                      {child.name}
                    </span>
                    {child.note && <span style={{
          fontSize: '12px',
          color: 'var(--tw-prose-body, #9ca3af)'
        }}>
                        {child.note}
                      </span>}
                  </div>;
      }
      return renderNode(child, depth + 1);
    })}
          </div>}

        {node.fullWidth && <div style={{
      marginTop: '8px'
    }}>
            {node.fullWidth.map(item => <div key={item.name} style={{
      padding: '8px 12px',
      border: '1px solid ' + (colors[item.type] || colors.input).border,
      borderRadius: '6px',
      marginTop: '8px'
    }}>
                <span style={{
      fontFamily: 'var(--font-mono, monospace)',
      fontSize: '13px',
      color: (colors[item.type] || colors.input).text
    }}>
                  {item.name}
                </span>
              </div>)}
          </div>}

        {node.grid && <div style={{
      display: 'grid',
      gridTemplateColumns: 'repeat(2, 1fr)',
      gap: '8px',
      marginTop: '8px'
    }}>
            {node.grid.map(item => <div key={item.name} style={{
      padding: '8px 12px',
      border: '1px solid ' + (colors[item.type] || colors.input).border,
      borderRadius: '6px'
    }}>
                <span style={{
      fontFamily: 'var(--font-mono, monospace)',
      fontSize: '13px',
      color: (colors[item.type] || colors.input).text
    }}>
                  {item.name}
                </span>
              </div>)}
          </div>}

        {node.bottomFullWidth && <div style={{
      marginTop: '8px'
    }}>
            {node.bottomFullWidth.map(item => <div key={item.name} style={{
      padding: '8px 12px',
      border: '1px solid ' + (colors[item.type] || colors.action).border,
      borderRadius: '6px',
      marginTop: '8px'
    }}>
                <span style={{
      fontFamily: 'var(--font-mono, monospace)',
      fontSize: '13px',
      color: (colors[item.type] || colors.action).text
    }}>
                  {item.name}
                </span>
              </div>)}
          </div>}

        {node.bottomGrid && <div style={{
      display: 'grid',
      gridTemplateColumns: 'repeat(2, 1fr)',
      gap: '8px',
      marginTop: '12px'
    }}>
            {node.bottomGrid.map(item => <div key={item.name} style={{
      padding: '10px 14px',
      border: '1px solid ' + (colors[item.type] || colors.utility).border,
      borderRadius: '6px'
    }}>
                <span style={{
      fontFamily: 'var(--font-mono, monospace)',
      fontSize: '13px',
      color: (colors[item.type] || colors.utility).text
    }}>
                  {item.name}
                </span>
              </div>)}
          </div>}
      </div>;
  };
  return <div style={{
    padding: '20px',
    borderRadius: '8px',
    border: '1px solid var(--tw-prose-hr, #e5e7eb)'
  }}>
      {renderNode(data)}
    </div>;
};

Primer Checkout provides a set of components that work together to create a complete payment experience. This page gives you an overview of all available components and how they relate to each other.

## Component Hierarchy

<Tabs>
  <Tab title="Web">
    The components follow a hierarchical structure where parent components provide context to their children. Each parent literally contains its children. The spatial nesting mirrors the DOM nesting.

    <Tabs>
      <Tab title="Without vault">
        <ComponentHierarchyBox data={CHECKOUT_HIERARCHY_WITHOUT_VAULT} />
      </Tab>

      <Tab title="With vault">
        <ComponentHierarchyBox data={CHECKOUT_HIERARCHY_WITH_VAULT} />
      </Tab>
    </Tabs>
  </Tab>

  <Tab title="Android">
    The components follow a hierarchical structure where the checkout controller manages presentation components, which in turn contain SDK UI elements.

    <Tabs>
      <Tab title="CheckoutSheet">
        <ComponentHierarchyBox data={ANDROID_CHECKOUT_HIERARCHY_SHEET} />
      </Tab>

      <Tab title="CheckoutHost">
        <ComponentHierarchyBox data={ANDROID_CHECKOUT_HIERARCHY_HOST} />
      </Tab>
    </Tabs>
  </Tab>

  <Tab title="iOS">
    The iOS SDK uses a SwiftUI slot hierarchy. The managed `PrimerCheckout` view renders the full flow, while `PrimerCheckoutSession` (attached with the `.primerCheckoutSession` modifier) injects the session that composable views such as `PrimerCardForm` and `PrimerPaymentMethods` resolve from the environment.

    <ComponentHierarchyBox data={IOS_CHECKOUT_HIERARCHY} />
  </Tab>
</Tabs>

## Core Components

<Tabs>
  <Tab title="Web">
    | Component                                                                                                  | Purpose                                                               |
    | ---------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------- |
    | [`<primer-checkout>`](/docs/sdk/primer-checkout-web/components/primer-checkout)                                 | Root component that initializes the SDK and provides checkout context |
    | [`<primer-main>`](/docs/sdk/primer-checkout-web/components/primer-main)                                         | Manages checkout states and provides layout customization             |
    | [`<primer-payment-method>`](/docs/sdk/primer-checkout-web/components/primer-payment-method)                     | Renders a specific payment method (card, PayPal, Apple Pay, etc.)     |
    | [`<primer-payment-method-container>`](/docs/sdk/primer-checkout-web/components/primer-payment-method-container) | Container for filtering and organizing multiple payment methods       |
  </Tab>

  <Tab title="Android">
    ### Checkout controllers

    | Function                                    | Returns                                 | Purpose                     |
    | ------------------------------------------- | --------------------------------------- | --------------------------- |
    | `rememberPrimerCheckoutController()`        | `PrimerCheckoutController`              | Main session controller     |
    | `rememberCardFormController()`              | `PrimerCardFormController`              | Card form state and actions |
    | `rememberPaymentMethodsController()`        | `PrimerPaymentMethodsController`        | Available payment methods   |
    | `rememberVaultedPaymentMethodsController()` | `PrimerVaultedPaymentMethodsController` | Saved payment methods       |

    ### Presentation components

    | Component              | Description                 | API Reference                                                                             |
    | ---------------------- | --------------------------- | ----------------------------------------------------------------------------------------- |
    | `PrimerCheckoutSheet`  | Modal bottom sheet checkout | [Reference](/docs/sdk/android-checkout/v3.0.0-beta/api/primer-checkout-sheet)                  |
    | `PrimerCheckoutHost`   | Inline checkout host        | [Reference](/docs/sdk/android-checkout/v3.0.0-beta/api/primer-checkout-host)                   |
    | `PrimerCardForm`       | Card payment form           | [Reference](/docs/sdk/android-checkout/v3.0.0-beta/api/card-form/primer-card-form)             |
    | `PrimerPaymentMethods` | Payment method list         | [Reference](/docs/sdk/android-checkout/v3.0.0-beta/api/payment-methods/primer-payment-methods) |
  </Tab>

  <Tab title="iOS">
    ### Entry points

    | Component                                          | Description                                             | API Reference                                                            |
    | -------------------------------------------------- | ------------------------------------------------------- | ------------------------------------------------------------------------ |
    | `PrimerCheckout`                                   | Managed SwiftUI view that renders the full default flow | [Reference](/docs/sdk/ios-checkout/v3.0.0-beta/api/primer-checkout)           |
    | `PrimerCheckoutSession` + `.primerCheckoutSession` | Session owner for inline, composable SwiftUI layouts    | [Reference](/docs/sdk/ios-checkout/v3.0.0-beta/api/primer-checkout-session)   |
    | `PrimerCheckoutPresenter`                          | UIKit presenter (modal sheet) with a delegate           | [Reference](/docs/sdk/ios-checkout/v3.0.0-beta/api/primer-checkout-presenter) |

    ### Composable views

    Each composable view exposes `@ViewBuilder` slots and resolves its session from the environment injected by `.primerCheckoutSession(_:onCompletion:)`.

    | Component                     | Description                                                                      | API Reference                                                                          |
    | ----------------------------- | -------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- |
    | `PrimerCardForm`              | Card payment form with `cardDetails`, `billingAddress`, and `submitButton` slots | [Reference](/docs/sdk/ios-checkout/v3.0.0-beta/api/card-form/primer-card-form)              |
    | `PrimerPaymentMethods`        | Available payment method list with `header`, `method`, and `emptyState` slots    | [Reference](/docs/sdk/ios-checkout/v3.0.0-beta/api/payment-methods/primer-payment-methods)  |
    | `PrimerVaultedPaymentMethods` | Saved (vaulted) payment methods with `header`, `item`, and `submitButton` slots  | [Reference](/docs/sdk/ios-checkout/v3.0.0-beta/api/payment-methods/vaulted-payment-methods) |
  </Tab>
</Tabs>

## Card Form Components

<Tabs>
  <Tab title="Web">
    | Component                                                                                                               | Purpose                                         |
    | ----------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------- |
    | [`<primer-card-form>`](/docs/sdk/primer-checkout-web/components/primer-card-form/primer-card-form)                           | Container that provides context for card inputs |
    | [`<primer-input-card-number>`](/docs/sdk/primer-checkout-web/components/primer-card-form/primer-input-card-number)           | Secure hosted input for card number             |
    | [`<primer-input-card-expiry>`](/docs/sdk/primer-checkout-web/components/primer-card-form/primer-input-card-expiry)           | Secure hosted input for expiry date             |
    | [`<primer-input-cvv>`](/docs/sdk/primer-checkout-web/components/primer-card-form/primer-input-cvv)                           | Secure hosted input for CVV                     |
    | [`<primer-input-card-holder-name>`](/docs/sdk/primer-checkout-web/components/primer-card-form/primer-input-card-holder-name) | Input for cardholder name                       |
    | [`<primer-card-form-submit>`](/docs/sdk/primer-checkout-web/components/primer-card-form/primer-card-form-submit)             | Submit button with built-in loading states      |
    | [`<primer-input-label>`](/docs/sdk/primer-checkout-web/components/primer-card-form/primer-input-label)                       | Label component for card inputs                 |
    | [`<primer-input-wrapper>`](/docs/sdk/primer-checkout-web/components/primer-card-form/primer-input-wrapper)                   | Wrapper for styling and layout of inputs        |
    | [`<primer-billing-address>`](/docs/sdk/primer-checkout-web/components/primer-card-form/primer-billing-address)               | Billing address collection form                 |
  </Tab>

  <Tab title="Android">
    ### Defaults objects

    | Object                        | Parent Component       | Sub-components                                                                                                                          |
    | ----------------------------- | ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
    | `PrimerCheckoutSheetDefaults` | `PrimerCheckoutSheet`  | `Splash`, `Loading`, `Success`, `Error`, `PaymentMethodSelection`, `VaultedMethods`, `PaymentMethods`                                   |
    | `CardFormDefaults`            | `PrimerCardForm`       | `CardNumberField`, `ExpiryField`, `CvvField`, `CardholderField`, `CardNetworkField`, `CountryCodeField`, billing fields, `SubmitButton` |
    | `PaymentMethodsDefaults`      | `PrimerPaymentMethods` | `SectionHeader`, `Method`, `EmptyState`                                                                                                 |

    ### State types

    | Type                             | Purpose                                 | Values                                   |
    | -------------------------------- | --------------------------------------- | ---------------------------------------- |
    | `PrimerCheckoutState`            | Checkout lifecycle and payment outcomes | `Loading`, `Ready`, `Success`, `Failure` |
    | `PrimerCardFormController.State` | Card form state                         | Field values, validation, loading        |
  </Tab>

  <Tab title="iOS">
    ### Defaults namespaces

    Each composable view falls back to its `Defaults` namespace when a slot is omitted. You can also call these helpers inside your own slots to keep the SDK rendering while adding custom layout.

    | Namespace                       | Parent component              | Section helpers                                 | Per-field building blocks                                                                                                                                                                         |
    | ------------------------------- | ----------------------------- | ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
    | `CardFormDefaults`              | `PrimerCardForm`              | `cardDetails`, `billingAddress`, `submitButton` | `cardNumber`, `expiryDate`, `cvv`, `cardholderName`, `cardNetwork`, `countryCode`, `firstName`, `lastName`, `addressLine1`, `addressLine2`, `city`, `state`, `postalCode`, `phoneNumber`, `email` |
    | `PaymentMethodsDefaults`        | `PrimerPaymentMethods`        | `header`, `method`, `emptyState`                | —                                                                                                                                                                                                 |
    | `VaultedPaymentMethodsDefaults` | `PrimerVaultedPaymentMethods` | `header`, `item`, `submitButton`                | —                                                                                                                                                                                                 |

    Each `CardFormDefaults` per-field block has the signature `(PrimerCardFormSession) -> some View` and is self-hiding: it renders nothing unless its field is part of the active `CardFormConfiguration`.

    ### Sessions

    | Session                  | Purpose                     | Key members                                                                                   |
    | ------------------------ | --------------------------- | --------------------------------------------------------------------------------------------- |
    | `PrimerCardFormSession`  | Card form state and actions | `state`, `updateCardNumber(_:)`, `updateCountryCode(_:)`, `selectCardNetwork(_:)`, `submit()` |
    | `PrimerSelectionSession` | Payment method selection    | `state`, `select(_:)`, `selectVaulted(_:)`, `vaultedPaymentMethods`                           |

    ### State types

    | Type                                | Purpose                        | Key properties                                                                      |
    | ----------------------------------- | ------------------------------ | ----------------------------------------------------------------------------------- |
    | `PrimerCheckoutState`               | Checkout lifecycle and outcome | `.initializing`, `.ready`, `.success`, `.failure`, `.dismissed`                     |
    | `PrimerCardFormState`               | Card form state                | `isValid`, `isLoading`, `fieldErrors`, `selectedNetwork`, `selectedCountry`, `data` |
    | `PrimerPaymentMethodSelectionState` | Payment method list            | Available and selected payment methods                                              |

    ### Field presence and styling

    There is no per-field configuration or styling struct. Which fields appear is Dashboard-driven through `CardFormConfiguration` (read from `session.state.configuration`), and visual styling is theme-driven via `PrimerCheckoutTheme` design tokens. To recompose a layout, arrange the `CardFormDefaults` per-field building blocks inside your own slot:

    ```swift theme={"dark"}
    PrimerCardForm(
      cardDetails: { session in
        VStack(spacing: 12) {
          CardFormDefaults.cardNumber(session)
          HStack(spacing: 12) {
            CardFormDefaults.expiryDate(session)
            CardFormDefaults.cvv(session)
          }
          CardFormDefaults.cardholderName(session)
        }
      }
    )
    ```
  </Tab>
</Tabs>

## Utility Components

<Tabs>
  <Tab title="Web">
    | Component                                                                                                | Purpose                                          |
    | -------------------------------------------------------------------------------------------------------- | ------------------------------------------------ |
    | [`<primer-error-message-container>`](/docs/sdk/primer-checkout-web/components/primer-error-message-container) | Displays payment error messages                  |
    | [`<primer-vault-manager>`](/docs/sdk/primer-checkout-web/components/primer-vault-manager)                     | Manages saved payment methods                    |
    | [`<primer-show-other-payments>`](/docs/sdk/primer-checkout-web/components/primer-show-other-payments)         | Toggle button to show additional payment methods |
  </Tab>

  <Tab title="Android">
    ### Theming

    | Class               | Purpose              |
    | ------------------- | -------------------- |
    | `PrimerTheme`       | Root theme container |
    | `LightColorTokens`  | Light mode colors    |
    | `DarkColorTokens`   | Dark mode colors     |
    | `SpacingTokens`     | Spacing values       |
    | `TypographyTokens`  | Font styles          |
    | `RadiusTokens`      | Corner radius        |
    | `BorderWidthTokens` | Border widths        |
    | `SizeTokens`        | Component sizes      |
  </Tab>

  <Tab title="iOS">
    ### Theming

    | Struct                 | Purpose                              |
    | ---------------------- | ------------------------------------ |
    | `PrimerCheckoutTheme`  | Root theme container                 |
    | `ColorOverrides`       | Brand, text, border, and icon colors |
    | `RadiusOverrides`      | Corner radius values                 |
    | `SpacingOverrides`     | Spacing values                       |
    | `SizeOverrides`        | Component sizes                      |
    | `TypographyOverrides`  | Font styles and sizes                |
    | `BorderWidthOverrides` | Border widths                        |

    ### Slot closures

    Customization points are the `@ViewBuilder` slots on the composable views. Each slot receives the relevant session (or model) and returns your view.

    | Slot                                            | Receives                              | On                            |
    | ----------------------------------------------- | ------------------------------------- | ----------------------------- |
    | `cardDetails`, `billingAddress`, `submitButton` | `PrimerCardFormSession`               | `PrimerCardForm`              |
    | `header`, `emptyState`                          | `PrimerSelectionSession`              | `PrimerPaymentMethods`        |
    | `method`                                        | `CheckoutPaymentMethod`, `() -> Void` | `PrimerPaymentMethods`        |
    | `header`                                        | `PrimerSelectionSession`              | `PrimerVaultedPaymentMethods` |
    | `item`                                          | `VaultedMethod`, `Bool`, `() -> Void` | `PrimerVaultedPaymentMethods` |
    | `submitButton`                                  | `Bool`, `Bool`, `() -> Void`          | `PrimerVaultedPaymentMethods` |
  </Tab>
</Tabs>

## Basic Usage

<Tabs>
  <Tab title="Web">
    ```html theme={"dark"}
    <primer-checkout client-token="your-client-token">
      <primer-main slot="main">
        <div slot="payments">
          <!-- Payment methods appear here -->
          <primer-payment-method type="PAYMENT_CARD"></primer-payment-method>
          <primer-payment-method type="PAYPAL"></primer-payment-method>

          <!-- Error display -->
          <primer-error-message-container></primer-error-message-container>
        </div>

        <div slot="checkout-complete">
          <h2>Payment successful!</h2>
        </div>
      </primer-main>
    </primer-checkout>
    ```
  </Tab>

  <Tab title="Android">
    ```kotlin theme={"dark"}
    @Composable
    fun CheckoutScreen(clientToken: String) {
        val checkout = rememberPrimerCheckoutController(clientToken)
        val state by checkout.state.collectAsStateWithLifecycle()

        LaunchedEffect(state) {
            when (val s = state) {
                is PrimerCheckoutState.Success -> navigateToConfirmation(s.checkoutData)
                is PrimerCheckoutState.Failure -> {
                    Log.e("Checkout", "Failed: ${s.error.description}")
                }
                else -> Unit
            }
        }

        PrimerCheckoutSheet(
            checkout = checkout,
            onDismiss = { navigateBack() },
        )
    }
    ```
  </Tab>

  <Tab title="iOS">
    ```swift theme={"dark"}
    struct CheckoutView: View {
      let clientToken: String

      var body: some View {
        PrimerCheckout(
          clientToken: clientToken,
          primerSettings: PrimerSettings(paymentHandling: .auto),
          primerTheme: PrimerCheckoutTheme(
            colors: ColorOverrides(primerColorBrand: .blue)
          ),
          onCompletion: { state in
            switch state {
            case .success(let result):
              print("Payment: \(result.paymentId)")
            case .failure(let error):
              print("Failed: \(error.errorId)")
            case .dismissed:
              print("Dismissed")
            default:
              break
            }
          }
        )
      }
    }
    ```
  </Tab>
</Tabs>

## How Components Communicate

Components communicate through:

1. **Context inheritance** - Child components receive context from their parents (e.g., card inputs receive form context from their parent card form)
2. **Events** - Components emit events that you can listen to for state changes
3. **Customization points** (Slots on Web, Composable lambdas on Android, `@ViewBuilder` slot closures on iOS) - Allow you to inject custom content at specific points

<Info>
  For details on events, see the [Events guide](/docs/checkout/primer-checkout/configuration/events).
</Info>

## Customization at Each Level

<Tabs>
  <Tab title="Web">
    * **`<primer-checkout>`** - SDK options, theming, global configuration
    * **`<primer-main>`** - Layout structure through customization points
    * **`<primer-payment-method>`** - Payment method-specific options
    * **`<primer-card-form>`** - Complete control over card form layout and styling
  </Tab>

  <Tab title="Android">
    * **`PrimerCheckoutSheet` / `PrimerCheckoutHost`** - Presentation mode and event handling
    * **`PrimerCheckoutSheetDefaults`** - Override default screens (splash, loading, success, error)
    * **`CardFormDefaults`** - Customize individual card form fields
    * **`PrimerTheme`** - Colors, typography, spacing, and border radius
  </Tab>

  <Tab title="iOS">
    * **`PrimerCheckout`** - Client token, settings, theme, and completion handling for the managed flow
    * **`PrimerCheckoutSession` + `.primerCheckoutSession`** - Inline, composable layouts and terminal-state handling
    * **`PrimerPaymentMethods`** - `header`, `method`, and `emptyState` slots for the payment method list
    * **`PrimerCardForm`** - `cardDetails`, `billingAddress`, and `submitButton` slots, recomposed from `CardFormDefaults`
    * **`PrimerCheckoutTheme`** - Colors, typography, spacing, radius, and border widths
  </Tab>
</Tabs>

## See also

<CardGroup cols={2}>
  <Card title="Using Slots" icon="table-layout" href="/docs/checkout/primer-checkout/build-your-ui/layout-customization">
    Learn how to customize layout with slots
  </Card>

  <Card title="Build a custom card form" icon="credit-card" href="/docs/checkout/primer-checkout/guides-and-recipes/build-custom-card-form">
    Step-by-step tutorial for card form customization
  </Card>

  <Card title="Styles" icon="palette" href="/docs/checkout/primer-checkout/build-your-ui/styling-customization">
    Customize appearance with CSS variables
  </Card>

  <Card title="SDK Reference" icon="code" href="/docs/sdk/primer-checkout-web/components/primer-checkout">
    Detailed API documentation for all components
  </Card>
</CardGroup>
