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

# Core Concepts

> Understand the component model, styling approach, and event-driven architecture

## What is a Primer Checkout component?

Primer Checkout is built from reusable UI components.

Each component represents a specific part of the checkout experience — such as the payment method selector, the card form, or the pay button — and is designed to be **configured, styled, and combined** without rewriting payment logic.

You don't build a checkout from scratch. You assemble and customize trusted building blocks.

## What this means for you

Using Primer Checkout components allows you to:

* Integrate a full checkout UI without managing sensitive payment inputs
* Customize layout and appearance without breaking functionality
* Keep a predictable and stable integration over time
* Upgrade components without refactoring your application

## Styling model

<Tabs>
  <Tab title="Web">
    Each Primer component uses **Shadow DOM**, which isolates its internal HTML and CSS from your application.

    You **cannot** style internal component elements with CSS selectors. Instead, Primer exposes **CSS variables** as the official customization API:

    ```css theme={"dark"}
    :root {
      --primer-color-brand: #6c5ce7;
      --primer-radius-small: 6px;
    }
    ```

    This allows safe customization, consistent theming, and runtime updates without rebuilding components.
  </Tab>

  <Tab title="Android">
    Styling is controlled through **design tokens** via the `PrimerTheme` data class. Tokens cover colors, spacing, typography, and shapes:

    ```kotlin theme={"dark"}
    val theme = PrimerTheme(
        lightColorTokens = object : LightColorTokens() {
            override val primerColorBrand: Color = Color(0xFF6C5CE7)
        },
        radiusTokens = RadiusTokens(medium = 12.dp),
    )

    PrimerCheckoutSheet(checkout = checkout, theme = theme)
    ```

    Token changes propagate to all SDK components automatically through `CompositionLocal`. This means you can apply a custom theme, and all Primer components will inherit the values.
  </Tab>

  <Tab title="iOS">
    Styling is controlled through **design tokens** via the `PrimerCheckoutTheme` class. Tokens cover colors, spacing, typography, radius, sizes, and border widths:

    ```swift theme={"dark"}
    let theme = PrimerCheckoutTheme(
      colors: ColorOverrides(
        primerColorBrand: .init(.systemIndigo)
      ),
      radius: RadiusOverrides(
        primerRadiusMedium: 12
      )
    )

    PrimerCheckout(clientToken: clientToken, primerTheme: theme)
    ```

    Token changes propagate to all SDK components automatically.
  </Tab>
</Tabs>

## Content and layout customization

Each platform provides a way to customize the content and layout of checkout components.

<Tabs>
  <Tab title="Web">
    Slots are named placeholders inside Web Components:

    ```html theme={"dark"}
    <primer-checkout client-token="...">
      <primer-main slot="main">
        <primer-card-form slot="cardForm"></primer-card-form>
        <primer-payment-methods slot="paymentMethods"></primer-payment-methods>
      </primer-main>
    </primer-checkout>
    ```
  </Tab>

  <Tab title="Android">
    Slots are Compose lambda parameters on SDK composables:

    ```kotlin theme={"dark"}
    PrimerCheckoutSheet(
        checkout = checkout,
        cardForm = {
            val controller = rememberCardFormController(checkout)
            PrimerCardForm(
                controller = controller,
                submitButton = { /* your custom button */ },
            )
        },
        success = { data -> /* your custom success screen */ },
    )
    ```
  </Tab>

  <Tab title="iOS">
    Slots are **scope properties** — typed closures where you provide your own SwiftUI views:

    ```swift theme={"dark"}
    PrimerCheckout(
      clientToken: clientToken,
      scope: { checkoutScope in
        // Customize the loading screen
        checkoutScope.loadingScreen = {
          AnyView(ProgressView("Processing payment..."))
        }

        // Customize the card form layout
        if let cardScope = checkoutScope.getPaymentMethodScope(
          PrimerCardFormScope.self
        ) {
          cardScope.submitButton = {
            AnyView(Text("Complete Purchase"))
          }
        }
      }
    )
    ```
  </Tab>
</Tabs>

## Communication: events

Primer Checkout uses an **event-driven architecture** to communicate with your application.

<Tabs>
  <Tab title="Web">
    Standard DOM events work with any framework:

    ```javascript theme={"dark"}
    const checkout = document.querySelector('primer-checkout');

    checkout.addEventListener('primer:payment-success', (event) => {
      const { payment } = event.detail;
      window.location.href = `/confirmation?order=${payment.orderId}`;
    });
    ```

    | Event                    | When it fires                  |
    | ------------------------ | ------------------------------ |
    | `primer:ready`           | SDK is initialized and ready   |
    | `primer:state-change`    | Checkout state changes         |
    | `primer:payment-success` | Payment completed successfully |
    | `primer:payment-failure` | Payment failed                 |

    For details, see the [Events Guide](/checkout/primer-checkout/configuration/events).
  </Tab>

  <Tab title="Android">
    Events are delivered through the `onEvent` callback as a sealed interface:

    ```kotlin theme={"dark"}
    PrimerCheckoutSheet(
        checkout = checkout,
        onEvent = { event ->
            when (event) {
                is PrimerCheckoutEvent.Success -> { /* payment completed */ }
                is PrimerCheckoutEvent.Failure -> { /* payment failed */ }
            }
        },
    )
    ```

    | Event                         | When it fires                    |
    | ----------------------------- | -------------------------------- |
    | `PrimerCheckoutEvent.Success` | Payment completed                |
    | `PrimerCheckoutEvent.Failure` | Payment failed or error occurred |
  </Tab>

  <Tab title="iOS">
    Events are delivered through two mechanisms: `AsyncStream` for state observation and `onCompletion` for terminal results:

    ```swift theme={"dark"}
    PrimerCheckout(
      clientToken: clientToken,
      scope: { checkoutScope in
        Task {
          for await state in checkoutScope.state {
            switch state {
            case .ready(let totalAmount, let currencyCode):
              print("Ready: \(totalAmount) \(currencyCode)")
            default:
              break
            }
          }
        }
      },
      onCompletion: { state in
        switch state {
        case .success(let result):
          print("Payment completed: \(result.payment?.id ?? "")")
        case .failure(let error):
          print("Payment failed: \(error.errorId)")
        case .dismissed:
          print("Dismissed")
        default:
          break
        }
      }
    )
    ```

    | Mechanism                          | When it fires                                  |
    | ---------------------------------- | ---------------------------------------------- |
    | `AsyncStream<PrimerCheckoutState>` | Every state change (initializing, ready, etc.) |
    | `onCompletion`                     | Terminal state (success, failure, dismissed)   |

    For details, see the [Events Guide](/checkout/primer-checkout/configuration/events).
  </Tab>
</Tabs>

## What the SDK handles

Primer Checkout handles this for you:

* Payment method logic and processor communication
* Input validation and error display
* 3DS challenges and redirect flows
* Loading states and visual feedback
* Security and PCI compliance

You focus on integration and UX, not infrastructure.

## Key takeaways

* Primer Checkout is built from reusable, isolated components
* Customization happens through configuration, layout customization, and styling tokens
* Events provide decoupled communication between your app and the SDK

## See also

<CardGroup cols={2}>
  <Card title="Styling" icon="palette" href="/checkout/primer-checkout/build-your-ui/styling-customization">
    Customize colors, fonts, and spacing
  </Card>

  <Card title="Layout" icon="table-layout" href="/checkout/primer-checkout/build-your-ui/layout-customization">
    Control component arrangement and layout
  </Card>

  <Card title="Events" icon="bolt" href="/checkout/primer-checkout/configuration/events">
    Handle payment lifecycle events
  </Card>

  <Card title="Components" icon="puzzle-piece" href="/checkout/primer-checkout/configuration/components">
    Explore all available components
  </Card>
</CardGroup>
