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: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 forprimer: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:- CamelCase props are lowercased. Writing
<primer-checkout clientToken="..." />sets the attributeclienttoken(all lowercase), which the component doesn’t recognize. - Objects are stringified. In React 18, passing an object prop like
options={{ locale: 'en-GB' }}results in the attributeoptions="[object Object]".
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
Thecustom-styles attribute accepts a JSON string. Pass it as a kebab-case attribute:
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:Vue 3
Vue has excellent Web Component support — it scores 100% on the Custom Elements Everywhere tests. Vue 3 automatically detects DOM properties using thein 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 treatprimer-* tags as custom elements:
- Vite (vite.config.js)
- Vue CLI (vue.config.js)
- In-Browser
<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
Vue 3 + SSR (Nuxt 3)
Primer requires browser APIs. In Nuxt 3, load it client-side only:nuxt.config.ts:
Other frameworks
Svelte
Svelte treats custom elements natively — no special configuration is needed.Angular
Angular supports custom elements throughCUSTOM_ELEMENTS_SCHEMA:
Migration checklist
Use this checklist to verify your migration is complete:- Updated
client-tokensetting — UsingsetAttribute()(vanilla JS), kebab-case attribute (React/Vue), or framework-appropriate binding - Updated
optionssetting — Using direct property assignment, notsetAttribute() - Updated
custom-styles— Passing a JSON string via attribute, or using CSS variables instead - Updated event handlers — Listening for
primer:payment-successandprimer:payment-failureevents - Configured compiler (Vue only) — Added
isCustomElementforprimer-*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