Skip to main content

Quick diagnosis

Server-side rendering errors

Error: “customElements is not defined”

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.

Error: “window is not defined”

Cause: Code is accessing browser globals during server-side rendering. Solution: Add environment checks before accessing browser APIs:

Card form issues

Duplicate card forms

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:

Card inputs not working

Cause: Card input components placed outside <primer-card-form>. Solution: All card inputs must be descendants of the card form:

Dynamic rendering creates duplicates

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:

React-specific issues

Options not being applied

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.
Solution: Use stable references:

Calling methods before ready

Cause: Calling SDK methods before the checkout is initialized. Solution: Wait for the primer:ready event:

React 18 vs React 19 property assignment

Cause: React 18 converts object props to [object Object] strings for web components. Solution: Use the appropriate pattern for your React version:

Styling issues

CSS not applying to components

Cause: Trying to style internal elements with CSS selectors. Shadow DOM prevents external CSS from reaching internal elements. Solution: Use CSS variables instead:

Flash of Unstyled Custom Elements

Cause: <primer-checkout> is a custom element. Until the browser has registered it (which happens when loadPrimer() actually runs), it has no shadow DOM and no styling of its own. Any content you’ve slotted into it renders as plain, unstyled HTML in the meantime. This is only a visible problem when both of these are true:
  • You’ve slotted your own content into the main slot — for example the custom content during loading pattern, where a <div slot="main"> wraps your own markup alongside <primer-main>
  • loadPrimer() runs after the page has already painted — for example inside a useEffect, behind a dynamic import(), or after other startup work
If you haven’t slotted anything, there’s nothing to flash. If loadPrimer() runs immediately, the window is too short to notice. It’s the combination that causes a visible flash. Solution: Add a CSS rule to your page — inline in <head>, not dependent on the SDK’s script having loaded — that targets the element until it’s actually defined:
:not(:defined) is true the instant the browser parses an unrecognized custom element — it needs no JavaScript and doesn’t depend on loadPrimer() having run, so it closes the gap regardless of how late the SDK loads. The rule above is just one example — display: none on the same selector works too. What matters is targeting :not(:defined), not which specific property you use to hide the content.
Also call loadPrimer() as early as your framework allows (e.g. before your first render) rather than inside a post-mount effect, to keep the window as short as possible.

Validation vs payment errors

Understanding the difference helps with proper error handling:
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.

Debugging tips

Log all Primer events

Verify component registration

Check available payment methods

Getting help

When contacting Primer support, include:
  1. The diagnosticsId from any error callbacks
  2. Your browser, OS version, and framework version
  3. Steps to reproduce the issue

See also

SSR guide

Server-side rendering patterns

React integration

React-specific guidance

Events guide

Event handling patterns

Build a custom card form

Card form tutorial