Card Form Component
<primer-card-form>
The CardForm component serves as a container for card input components. It handles payment card form submission, validation, and provides context to child components through a context provider system.
Component Architecture
The Card Form component functions as both a container and a context provider. It manages the following:
- 
Context Provision: Creates and provides a context that contains:
- Secure hosted input elements for card data
 - Validation state management
 - Form state information
 - Submission handlers
 
 - 
Form Management: Handles HTML form creation, submission events, and validation flows
 - 
Component Orchestration: Coordinates all card input components to work together seamlessly
 - 
Layout Options: Provides both default and customizable layouts through the slot system
 
Component Hierarchy
The Card Form component has a specific relationship with its child components:
The relationship is structured as follows:
- 
Parent-Child Relationship: All card input components must be children of the
primer-card-form, either:- Through the default layout (when no custom content is provided)
 - Through custom content in the 
card-form-contentslot 
 - 
Context-Consumer Relationship: All card input components consume the context provided by
primer-card-form:- Input fields receive hosted input instances
 - Components receive validation state
 - Components can update shared card form data
 
 - 
Coordination Role: The card form coordinates all aspects of the payment form, including:
- Creation of secure input fields
 - Validation of card data
 - Submission to payment processors
 - Error handling and reporting
 
 
Technical Implementation
The Card Form component:
- Creates a context provider that supplies hosted input elements to child components
 - Uses 
display: contentsto seamlessly integrate with parent layout without creating a new box in the DOM - Manages hosted inputs by creating them through the Payment Card manager
 - Handles form submission through multiple detection methods (native buttons, custom buttons, and direct events)
 - Provides default layout when no custom content is provided
 
Usage
The CardForm component can be used in two ways:
<primer-card-form></primer-card-form>
This renders a complete card form with:
- Card number input
 - Expiry date input
 - CVV input
 - Cardholder name input
 - Submit button
 
<primer-card-form disabled>
  <div slot="card-form-content">
    <primer-input-card-holder-name></primer-input-card-holder-name>
    <primer-input-card-number></primer-input-card-number>
    <div style="display: flex; gap: 8px;">
      <primer-input-card-expiry></primer-input-card-expiry>
      <primer-input-cvv></primer-input-cvv>
    </div>
    <button type="submit">Pay Now</button>
  </div>
</primer-card-form>
DOM Structure
When no custom content is provided, the component renders the following DOM structure:
<form>
  <div class="card-form">
    <primer-input-card-number></primer-input-card-number>
    <div class="card-form-row">
      <primer-input-card-expiry></primer-input-card-expiry>
      <primer-input-cvv></primer-input-cvv>
    </div>
    <primer-input-card-holder-name></primer-input-card-holder-name>
  </div>
  <primer-card-form-submit></primer-card-form-submit>
</form>
With custom content, your slotted content replaces the default structure.
Properties
| Name | Type | Description | Default | 
|---|---|---|---|
disabled | Boolean | When true, disables the card form and prevents submission | false | 
Slots
| Name | Description | 
|---|---|
card-form-content | Custom content slot for the card form. When provided, it replaces the default card form layout. | 
Events
| Event Name | Type | Description | Event Detail | 
|---|---|---|---|
primer:card-submit | Triggerable | Triggers card form submission programmatically | CardSubmitPayload | 
primer:card-success | Listener | Fired when the form is successfully submitted | CardSubmitSuccessPayload | 
primer:card-error | Listener | Fired when there are validation errors during submission | CardSubmitErrorsPayload | 
Event Payload Interfaces
CardSubmitPayload (Triggerable Event)
interface CardSubmitPayload {
  source?: string; // Optional identifier of the trigger source
}
CardSubmitSuccessPayload
interface CardSubmitSuccessPayload {
  result: unknown; // The successful submission result
}
CardSubmitErrorsPayload
interface CardSubmitErrorsPayload {
  errors: unknown | InputValidationError[];
}
Disabled State
The disabled attribute prevents card form submission and makes the form non-interactive:
<!-- Disabled card form -->
<primer-card-form disabled></primer-card-form>
<!-- Conditionally disabled in JavaScript -->
<primer-card-form id="card-form"></primer-card-form>
<script>
  const cardForm = document.getElementById('card-form');
  // Disable during processing
  if (isProcessing) {
    cardForm.setAttribute('disabled', '');
  } else {
    cardForm.removeAttribute('disabled');
  }
</script>
When disabled:
- Form submission is prevented
 - Submit button becomes non-interactive
 - Input fields remain functional for data entry
 - Visual feedback is provided through the submit button
 
Form Submission
The CardForm component handles form submission automatically. You can trigger form submission in multiple ways:
1. Using the primer-card-form-submit component (Recommended)
<primer-card-form-submit></primer-card-form-submit>
This is the recommended approach as it provides localized button text and consistent styling.
2. Using a native HTML button
<button type="submit">Pay Now</button>
3. Using a primer-button component
<primer-button buttonType="submit">Pay Now</primer-button>
4. Using the data-submit attribute
<button data-submit>Pay Now</button>
<!-- or -->
<primer-button data-submit>Pay Now</primer-button>
5. Programmatically via primer:card-submit Event
You can trigger card form submission programmatically by dispatching a primer:card-submit event. The checkout component listens for this event at the document level, so you can dispatch it from anywhere in your application without needing to reference the card form element.
// Trigger card form submission from anywhere in your application
document.dispatchEvent(
  new CustomEvent('primer:card-submit', {
    bubbles: true,
    composed: true,
    detail: { source: 'external-button' },
  }),
);
The bubbles: true and composed: true properties are required. These properties allow the event to propagate correctly through the DOM and across shadow DOM boundaries, ensuring the checkout component can capture the event regardless of where it's dispatched.
Advanced Example: Custom Submit Button with Event Handling
<primer-card-form>
  <div slot="card-form-content">
    <primer-input-card-number></primer-input-card-number>
    <primer-input-card-expiry></primer-input-card-expiry>
    <primer-input-cvv></primer-input-cvv>
    <button type="button" id="custom-submit" class="custom-pay-button">
      Pay Now
    </button>
  </div>
</primer-card-form>
<script>
  // Set up custom submit button
  document.getElementById('custom-submit').addEventListener('click', () => {
    // Dispatch to document - checkout listens at document level
    document.dispatchEvent(
      new CustomEvent('primer:card-submit', {
        bubbles: true,
        composed: true,
        detail: { source: 'custom-pay-button' },
      }),
    );
  });
  // Handle submission results
  const checkout = document.querySelector('primer-checkout');
  checkout.addEventListener('primer:card-success', (event) => {
    console.log('Payment successful:', event.detail.result);
    // Handle success
  });
  checkout.addEventListener('primer:card-error', (event) => {
    console.log('Validation errors:', event.detail.errors);
    // Handle errors
  });
</script>
Include a meaningful source identifier in the event detail. This helps with debugging and allows you to handle submissions differently based on the trigger source. The checkout component captures this event at the document level and forwards it internally to the card form.
For advanced event handling patterns with CardForm, including validation events, submission flows, and error management, see:
- Events Guide - Implementation patterns and best practices
 - Events & Callbacks Reference - Complete event API documentation
 
Validation
The CardForm component automatically handles validation of all card input fields. Validation occurs when:
- The form is submitted
 - Individual fields trigger validation events
 
Validation errors are automatically passed to the respective input components to display appropriate error messages. The validation process:
- Calls the 
validate()method on the card manager - If validation fails, updates the context with validation errors
 - Dispatches a 
primer:card-errorevent with the errors - Child components receive the errors and display appropriate messages
 
Context Provider
The CardForm component serves as a context provider for all child input components. It provides:
- Hosted Inputs: Secure iframe-based inputs for card number, expiry, and CVV
 - Setter Methods: Functions to update cardholder name and card network
 - Validation State: Current validation errors for each input
 - Submission Methods: Functions to submit the card payment
 
This context mechanism ensures secure handling of sensitive payment data.
Child Components
The CardForm component is designed to work with the following child components:
primer-input-card-number: For entering the card numberprimer-input-card-expiry: For entering the card expiry dateprimer-input-cvv: For entering the card CVV/security codeprimer-input-card-holder-name: For entering the cardholder's nameprimer-card-form-submit: A submit button component (used in the default layout)
Child Component Dependencies
All card form input components have a mandatory dependency on the primer-card-form context. They will not function correctly if used outside this context, as they require:
- Access to secure hosted inputs
 - The validation system
 - Card data aggregation for submission
 
Examples
Complete Checkout Flow with CardForm
<primer-checkout clientToken="your-client-token" options="{options}">
  <primer-main slot="main">
    <div slot="payments">
      <primer-card-form>
        <div slot="card-form-content">
          <primer-input-card-holder-name></primer-input-card-holder-name>
          <primer-input-card-number></primer-input-card-number>
          <div style="display: flex; gap: 8px;">
            <primer-input-card-expiry></primer-input-card-expiry>
            <primer-input-cvv></primer-input-cvv>
          </div>
          <button type="submit">Complete Payment</button>
        </div>
      </primer-card-form>
    </div>
  </primer-main>
</primer-checkout>
Handling Form Submission Events
const checkout = document.querySelector('primer-checkout');
// Listen for successful submissions
checkout.addEventListener('primer:card-success', (event) => {
  console.log('Payment successful!', event.detail.result);
  // Handle successful payment (e.g., show confirmation, redirect)
});
// Listen for validation errors
checkout.addEventListener('primer:card-error', (event) => {
  console.error('Validation errors:', event.detail.errors);
  // Handle errors (e.g., scroll to error, show notification)
});
CSS Custom Properties
The CardForm component uses the following CSS custom properties for styling:
| Property | Description | 
|---|---|
--primer-space-small | Spacing between inline elements (default: 8px) | 
--primer-space-medium | Spacing between block elements (default: 16px) | 
Co-branded Card Schemes Support
The <primer-card-form> component has been built to support co-branded card schemes like Cartes Bancaires (CB) natively. When the card networks detect a co-branded card, the network switcher will display automatically. No additional configuration is needed on the Primer.js side.
Key Considerations
- The CardForm component must be used within a 
primer-checkoutcomponent - All card input components must be placed inside a CardForm component to function properly
 - CardForm automatically manages the hosted input elements for secure card data collection
 - When disabled, the form prevents submission but input fields remain functional
 
- The component uses 
display: contentsto avoid creating additional DOM structure - When no custom content is provided, a default form layout is rendered
 - Submit buttons are detected based on their attributes (type="submit" or data-submit)
 - The 
disabledattribute is passed to the card form submit button through context