Cause: Primer code is running on the server where Web Components API doesn’t exist.Solution: Ensure loadPrimer() is called only in client-side lifecycle methods.
Cause: Using both <primer-card-form> and <primer-payment-method type="PAYMENT_CARD"> in the same layout.Why this happens:<primer-payment-method type="PAYMENT_CARD"> internally creates its own <primer-card-form>. When you also add a custom card form, you end up with two card forms on the page.Solution: Choose one approach:
Copy
Ask AI
<!-- Option A: Use custom card form only --><div slot="payments"> <primer-card-form> <div slot="card-form-content"> <!-- Your custom layout --> </div> </primer-card-form> <!-- Other payment methods, but NOT PAYMENT_CARD --> <primer-payment-method type="PAYPAL"></primer-payment-method></div><!-- Option B: Use payment method component only --><div slot="payments"> <!-- Let the component handle the card form --> <primer-payment-method type="PAYMENT_CARD"></primer-payment-method> <primer-payment-method type="PAYPAL"></primer-payment-method></div>
Cause: When dynamically rendering payment methods, PAYMENT_CARD is included while also using a custom card form.Solution: Filter out PAYMENT_CARD when using a custom card form:
Copy
Ask AI
checkout.addEventListener('primer:methods-update', (event) => { const availableMethods = event.detail .toArray() // Filter out PAYMENT_CARD if you're using a custom card form .filter((method) => method.type !== 'PAYMENT_CARD'); availableMethods.forEach((method) => { const element = document.createElement('primer-payment-method'); element.setAttribute('type', method.type); container.appendChild(element); });});
Cause: Creating new object references on every render forces unnecessary comparisons.
The Primer SDK implements deep comparison for the options property. This means unstable references won’t cause re-initialization, but they still add comparison overhead on every render.
Cause: Trying to style internal elements with CSS selectors. Shadow DOM prevents external CSS from reaching internal elements.Solution: Use CSS variables instead:
Copy
Ask AI
/* WRONG: Won't work with Shadow DOM */primer-checkout input { border-color: blue;}/* CORRECT: Use CSS variables */primer-checkout { --primer-color-border-input-default: blue;}
Understanding the difference helps with proper error handling:
Error Type
When It Occurs
How It’s Handled
Validation errors
During input (invalid format, missing fields)
Handled automatically by input components; prevents form submission
Payment failures
After form submission (declined card, network issues)
Requires explicit handling with error container or custom code
Don’t confuse these two error types. Validation errors prevent form submission and are shown inline. Payment failures occur after the form is submitted and require explicit handling.