@cubepay/react-radiumone-js
v1.0.0-beta.8
Published
RadiumOne Elements — React components and hooks
Downloads
247
Readme
@cubepay/react-radiumone-js
React components and hooks for RadiumOne Elements -- PCI-compliant card input fields that keep card data out of your React app entirely.
<RadiumOneProvider radiumone={promise}> -- SDK init + context
<CheckoutForm>
<CardElement /> -- iframe (js.radiumone.io)
| card data stays here
useElements() -> elements.submit() -- encrypt + tokenize
|
result.token -> your server -- only token crosses
</CheckoutForm>
</RadiumOneProvider>Installation
npm install @cubepay/radiumone-js @cubepay/react-radiumone-jsBoth packages are required — @cubepay/radiumone-js is a peer dependency.
Quick Start
import { useState } from 'react';
import { loadRadiumOne } from '@cubepay/radiumone-js';
import { RadiumOneProvider, CardElement, useElements } from '@cubepay/react-radiumone-js';
// Initialize outside component — loadRadiumOne caches by key
const radiumonePromise = loadRadiumOne('r1pk_prod_your_key');
function App() {
return (
<RadiumOneProvider radiumone={radiumonePromise}>
<CheckoutForm />
</RadiumOneProvider>
);
}
function CheckoutForm() {
const elements = useElements();
const [error, setError] = useState<string | null>(null);
const [complete, setComplete] = useState(false);
const [loading, setLoading] = useState(false);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!elements) return;
setLoading(true);
try {
// sessionId and sessionSecret come from your server
// (POST /v1/sessions with your secret key)
const result = await elements.submit(sessionId, sessionSecret);
// Send the token to your server to complete the payment
await fetch('/api/charge', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ token: result.token }),
});
} catch (err) {
setError(err instanceof Error ? err.message : 'Payment failed');
} finally {
setLoading(false);
}
};
return (
<form onSubmit={handleSubmit}>
<CardElement
onChange={(e) => {
setError(e.error?.message ?? null);
setComplete(e.complete);
}}
/>
{error && <p style={{ color: 'red' }}>{error}</p>}
<button type="submit" disabled={!complete || loading}>
{loading ? 'Processing...' : 'Pay'}
</button>
</form>
);
}Components
<RadiumOneProvider>
Context provider that initializes the SDK and creates an Elements instance. Wrap your checkout page with this.
<RadiumOneProvider
radiumone={radiumonePromise}
appearance={{ theme: 'flat', variables: { colorPrimary: '#6366f1' } }}
locale="en"
>
{children}
</RadiumOneProvider>| Prop | Type | Description |
|------|------|-------------|
| radiumone | Promise<RadiumOne \| null> | Promise from loadRadiumOne() |
| appearance | AppearanceOptions | Theme and styling (see core SDK docs) |
| locale | string | Locale for labels/errors (default: 'en', set once at mount) |
Appearance can be updated dynamically — the provider applies changes without recreating iframes.
<CardElement>
Combined card input — number, expiry, and CVV in a single field.
<CardElement
onChange={(e) => console.log(e.complete, e.error)}
onReady={() => console.log('Ready')}
onFocus={() => console.log('Focused')}
onBlur={() => console.log('Blurred')}
className="my-card-input"
id="card"
/>Split Field Components
For layouts that need separate card number, expiry, and CVV inputs:
import {
CardNumberElement,
CardExpiryElement,
CardCvvElement,
} from '@cubepay/react-radiumone-js';
function SplitCardForm() {
const [brand, setBrand] = useState('unknown');
return (
<div>
<label>Card number</label>
<CardNumberElement
onChange={(e) => { /* handle change */ }}
onCardTypeChange={(e) => setBrand(e.brand)}
/>
<div style={{ display: 'flex', gap: 16 }}>
<div style={{ flex: 1 }}>
<label>Expiry</label>
<CardExpiryElement onChange={(e) => { /* handle change */ }} />
</div>
<div style={{ flex: 1 }}>
<label>CVV</label>
<CardCvvElement onChange={(e) => { /* handle change */ }} />
</div>
</div>
</div>
);
}Tab navigation between split fields is handled automatically.
Element Props
All element components accept these props:
| Prop | Type | Description |
|------|------|-------------|
| onChange | (event: ChangeEvent) => void | Field value/validity changed |
| onReady | (event: ReadyEvent) => void | Iframe loaded, field is interactive |
| onFocus | (event: FocusEvent) => void | Field received focus |
| onBlur | (event: BlurEvent) => void | Field lost focus |
| options | CreateElementOptions | { showIcon, autoAdvance } — set once at mount |
| className | string | CSS class for the container <div> |
| id | string | ID for the container <div> |
| style | CSSProperties | Inline styles for the container <div> |
<CardNumberElement> also accepts:
| Prop | Type | Description |
|------|------|-------------|
| onCardTypeChange | (event: CardTypeChangeEvent) => void | Detected card brand changed |
All standard <div> HTML attributes are forwarded to the container element.
Hooks
useElements()
Returns the Elements instance (or null while loading / during SSR).
function PayButton() {
const elements = useElements();
const handlePay = async () => {
if (!elements) return;
const result = await elements.submit(sessionId, sessionSecret);
// result.token, result.lastFour, result.cardBrand
};
return <button onClick={handlePay} disabled={!elements}>Pay</button>;
}useRadiumOne()
Returns the RadiumOne SDK instance (or null). Rarely needed — use useElements() for most cases.
SSR / Next.js
Works out of the box with Next.js App Router and Pages Router.
- During server render, components render empty
<div>containers loadRadiumOne()returnsnullon the server (nowindow)- SDK initializes client-side only after hydration
useElements()anduseRadiumOne()returnnullduring SSR
// app/checkout/page.tsx (Next.js App Router)
'use client';
import { loadRadiumOne } from '@cubepay/radiumone-js';
import { RadiumOneProvider, CardElement } from '@cubepay/react-radiumone-js';
const radiumonePromise = loadRadiumOne('r1pk_prod_your_key');
export default function CheckoutPage() {
return (
<RadiumOneProvider radiumone={radiumonePromise}>
<CardElement onChange={(e) => { /* ... */ }} />
</RadiumOneProvider>
);
}TypeScript
Full type definitions included. Import types from either package:
import type { ChangeEvent, BindResult, CardBrand } from '@cubepay/radiumone-js';
import type { BaseElementProps, CardNumberElementProps } from '@cubepay/react-radiumone-js';Requirements
- React 18+ or React 19+
@cubepay/radiumone-js(peer dependency)- HTTPS (secure context required for Web Crypto API)
License
Proprietary. See LICENSE.md in the published package for details.
