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

# PrimerCardForm

> Composable component that renders a complete card payment form with customizable slots

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>;
};

<Warning>
  Primer Checkout Android SDK is currently in **beta** (v3.0.0-beta.4).
  The API is subject to change before the stable release.
</Warning>

Composable that renders a card payment form with card details, optional billing address, and submit button. Each section is a slot parameter you can replace.

## Signature

```kotlin theme={"dark"}
@Composable
fun PrimerCardForm(
    controller: PrimerCardFormController,
    modifier: Modifier = Modifier,
    cardDetails: @Composable () -> Unit = { CardFormDefaults.CardDetailsContent(controller) },
    billingAddress: @Composable () -> Unit = { CardFormDefaults.BillingAddressContent(controller) },
    submitButton: @Composable () -> Unit = { CardFormDefaults.SubmitButton(controller) },
)
```

## Parameters

| Parameter        | Type                                                                                               | Default                                              | Description                                                              |
| ---------------- | -------------------------------------------------------------------------------------------------- | ---------------------------------------------------- | ------------------------------------------------------------------------ |
| `controller`     | [`PrimerCardFormController`](/docs/sdk/android-checkout/v3.0.0-beta/api/card-form/card-form-controller) | Required                                             | Controller managing form state, validation, and submission               |
| `modifier`       | `Modifier`                                                                                         | `Modifier`                                           | Modifier applied to the root container                                   |
| `cardDetails`    | `@Composable () -> Unit`                                                                           | `CardFormDefaults.CardDetailsContent(controller)`    | Slot for card input fields (number, expiry, CVV, cardholder name)        |
| `billingAddress` | `@Composable () -> Unit`                                                                           | `CardFormDefaults.BillingAddressContent(controller)` | Slot for billing address fields. Only rendered when required by session. |
| `submitButton`   | `@Composable () -> Unit`                                                                           | `CardFormDefaults.SubmitButton(controller)`          | Slot for the submit/pay button                                           |

## Default layout

<ComponentHierarchyBox
  data={{
name: 'PrimerCardForm',
type: 'form',
fullWidth: [
{ name: 'CardNumberField', type: 'input' }
],
grid: [
{ name: 'ExpiryField', type: 'input' },
{ name: 'CvvField', type: 'input' }
],
bottomFullWidth: [
{ name: 'CardholderField', type: 'input' },
{ name: 'Billing address fields', type: 'utility' },
{ name: 'SubmitButton', type: 'action' }
]
}}
/>
