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.
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:
This allows safe customization, consistent theming, and runtime updates without rebuilding components.
Styling is controlled through design tokens via the PrimerTheme data class. Tokens cover colors, spacing, typography, and shapes:
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.
Styling is controlled through design tokens via the PrimerCheckoutTheme class. Tokens cover colors, spacing, typography, radius, sizes, and border widths:
Events are delivered through two mechanisms: AsyncStream for state observation and onCompletion for terminal results:
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 } })