Skip to main content
Primer Checkout is built with Lit Web Components. Each JavaScript framework handles Web Components differently — especially around setting properties, passing objects, and handling events. This guide provides the correct migration patterns for each framework.
This guide is for Web only. iOS and Android use native UI frameworks (SwiftUI/UIKit and Jetpack Compose respectively) and do not require framework-specific migration steps. See the Migration Guide for platform-specific migration instructions.
This guide covers framework-specific patterns. For the complete list of Web API and configuration changes (callbacks, payment method options, event payloads, vault methods), see the Migration Guide.

Quick comparison


Vanilla JavaScript

Vanilla JS has the most straightforward integration. The <primer-checkout> element behaves like any other DOM element.

Property types

Primer Checkout uses two types of configuration. The distinction matters:
Don’t mix these up. Component properties must use setAttribute(). The options object must be assigned directly. Using setAttribute('options', ...) will not work.

Migration example

Direct property assignment also works in vanilla JS. Because <primer-checkout> is a Lit element, you can use checkout.clientToken = '...' — Lit’s @property decorator maps between the JS property and the HTML attribute. However, the SDK documentation uses setAttribute() for string properties, and we recommend following that convention for consistency.

Intercepting payments

To run validation before payment creation, listen for primer:payment-start and use preventDefault():

React

React’s handling of Web Components differs significantly between React 18 and React 19, particularly for object properties.

Why React needs special handling

React assumes all JSX props map to HTML attributes — it has no built-in mechanism to set DOM properties on custom elements. This creates two problems:
  1. CamelCase props are lowercased. Writing <primer-checkout clientToken="..." /> sets the attribute clienttoken (all lowercase), which the component doesn’t recognize.
  2. Objects are stringified. In React 18, passing an object prop like options={{ locale: 'en-GB' }} results in the attribute options="[object Object]".
React 19 added native support for custom element properties, which resolves the object problem but not the attribute naming issue.

React 19

React 19 can pass objects directly as properties to custom elements.

React 18

React 18 cannot pass objects via JSX props — they become [object Object] strings. Use a ref to set the options property imperatively.

Custom styling in React

The custom-styles attribute accepts a JSON string. Pass it as a kebab-case attribute:
Alternatively, use CSS variables directly in your stylesheet (no custom-styles attribute needed):

TypeScript setup

TypeScript doesn’t recognize custom element tags. Add a declaration:

React + SSR (Next.js)

Primer requires browser APIs. In Next.js, load it client-side only:
→ See the SSR Guide and React Integration Guide for complete patterns.

Vue 3

Vue has excellent Web Component support — it scores 100% on the Custom Elements Everywhere tests. Vue 3 automatically detects DOM properties using the in operator and sets them correctly, so passing objects works naturally.

Compiler configuration

Vue attempts to resolve any tag with a dash as a Vue component first. Tell the compiler to treat primer-* tags as custom elements:
Without this configuration, Vue will emit “failed to resolve component” warnings for <primer-checkout> and other Primer elements.

Basic integration

Passing properties — Vue’s in operator check

Vue 3 automatically checks whether a key exists as a DOM property using the in operator. If it does, Vue sets it as a property (not an attribute). This means objects like options work correctly in most cases. However, if the in check fails (the property isn’t defined when Vue first renders), you can force property binding with the .prop modifier:
The .prop modifier (or its shorthand .) explicitly tells Vue to set a DOM property rather than an attribute. This is rarely needed for Primer components since Lit defines its properties upfront, but it’s a useful escape hatch.

Custom styling in Vue

Or use CSS variables in a style block (preferred):

Vue 3 + SSR (Nuxt 3)

Primer requires browser APIs. In Nuxt 3, load it client-side only:
You’ll also need to register Primer elements as custom elements in nuxt.config.ts:
→ See the SSR Guide for more Nuxt patterns.

Other frameworks

Svelte

Svelte treats custom elements natively — no special configuration is needed.

Angular

Angular supports custom elements through CUSTOM_ELEMENTS_SCHEMA:

Migration checklist

Use this checklist to verify your migration is complete:
  • Updated client-token setting — Using setAttribute() (vanilla JS), kebab-case attribute (React/Vue), or framework-appropriate binding
  • Updated options setting — Using direct property assignment, not setAttribute()
  • Updated custom-styles — Passing a JSON string via attribute, or using CSS variables instead
  • Updated event handlers — Listening for primer:payment-success and primer:payment-failure events
  • Configured compiler (Vue only) — Added isCustomElement for primer-* tags
  • SSR handled (if applicable) — Loading Primer client-side only

Next steps

General Migration Guide

API changes, payment method options, and event payloads

React Integration

React 18/19 patterns in depth

SSR Guide

Next.js, Nuxt, and SvelteKit

SDK Options Reference

Complete configuration reference