React components and hooks for Stripe.js and Elements payment processing integration
—
Complete embedded checkout solution for streamlined payment experiences. Provides a fully-hosted checkout flow that can be embedded directly into your application.
Context provider that manages the embedded checkout instance and configuration throughout your React application.
/**
* React context provider for Embedded Checkout
* @param props - Embedded checkout provider configuration props
* @returns JSX element wrapping child components with embedded checkout context
*/
function EmbeddedCheckoutProvider(props: EmbeddedCheckoutProviderProps): JSX.Element;
interface EmbeddedCheckoutProviderProps {
/** Stripe instance or Promise resolving to Stripe instance */
stripe: PromiseLike<Stripe | null> | Stripe | null;
/** Embedded checkout configuration options */
options: EmbeddedCheckoutOptions;
/** Child components that will have access to embedded checkout context */
children: ReactNode;
}
interface EmbeddedCheckoutOptions {
/** Client secret for the checkout session */
clientSecret?: string | null;
/** Function to fetch client secret dynamically */
fetchClientSecret?: (() => Promise<string>) | null;
/** Callback fired when checkout is completed successfully */
onComplete?: () => void;
/** Callback fired when checkout encounters an error */
onError?: (error: {message: string}) => void;
/** Callback fired when shipping details change */
onShippingDetailsChange?: (
event: StripeEmbeddedCheckoutShippingDetailsChangeEvent
) => Promise<ResultAction>;
/** Callback fired when line items change */
onLineItemsChange?: (
event: StripeEmbeddedCheckoutLineItemsChangeEvent
) => Promise<ResultAction>;
}The embedded checkout component that renders the complete checkout experience within your application.
/**
* Embedded checkout component that mounts the checkout experience
* @param props - Embedded checkout component props
* @returns JSX element for embedded checkout interface
*/
function EmbeddedCheckout(props: EmbeddedCheckoutProps): JSX.Element;
interface EmbeddedCheckoutProps {
/** HTML id attribute for the checkout container */
id?: string;
/** CSS class name for the checkout container */
className?: string;
}Basic Usage Examples:
import React, { useState, useCallback } from 'react';
import { loadStripe } from '@stripe/stripe-js';
import {
EmbeddedCheckoutProvider,
EmbeddedCheckout
} from '@stripe/react-stripe-js';
const stripePromise = loadStripe('pk_test_...');
const CheckoutForm = () => {
const [clientSecret, setClientSecret] = useState('');
// Fetch client secret from your backend
const fetchClientSecret = useCallback(async () => {
const response = await fetch('/api/create-checkout-session', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
// Your checkout session parameters
line_items: [
{
price: 'price_1234',
quantity: 1,
},
],
}),
});
const { client_secret } = await response.json();
return client_secret;
}, []);
const handleComplete = () => {
console.log('Checkout completed successfully!');
// Redirect to success page or show confirmation
window.location.href = '/checkout/success';
};
const handleError = (error) => {
console.error('Checkout error:', error.message);
// Handle checkout errors
};
return (
<div className="checkout-container">
<EmbeddedCheckoutProvider
stripe={stripePromise}
options={{
fetchClientSecret,
onComplete: handleComplete,
onError: handleError
}}
>
<EmbeddedCheckout className="embedded-checkout" />
</EmbeddedCheckoutProvider>
</div>
);
};
export default CheckoutForm;Advanced Usage with Static Client Secret:
import React from 'react';
import { loadStripe } from '@stripe/stripe-js';
import {
EmbeddedCheckoutProvider,
EmbeddedCheckout
} from '@stripe/react-stripe-js';
const stripePromise = loadStripe('pk_test_...');
const StaticCheckoutForm = ({ checkoutSessionClientSecret }) => {
const handleComplete = () => {
// Handle successful checkout
console.log('Payment completed!');
// You might want to:
// - Show a success message
// - Redirect to a confirmation page
// - Update your app state
// - Send analytics events
};
const handleError = (error) => {
// Handle checkout errors
console.error('Checkout failed:', error.message);
// You might want to:
// - Show an error message to the user
// - Log the error for debugging
// - Provide alternative payment options
// - Contact support information
};
return (
<div className="checkout-page">
<div className="checkout-header">
<h1>Complete Your Purchase</h1>
<p>Secure checkout powered by Stripe</p>
</div>
<EmbeddedCheckoutProvider
stripe={stripePromise}
options={{
clientSecret: checkoutSessionClientSecret,
onComplete: handleComplete,
onError: handleError
}}
>
<EmbeddedCheckout
id="checkout-form"
className="my-embedded-checkout"
/>
</EmbeddedCheckoutProvider>
<div className="checkout-footer">
<p>Questions? Contact support@example.com</p>
</div>
</div>
);
};Server-Side Rendering (SSR) Example:
import React, { useState, useEffect } from 'react';
import { loadStripe } from '@stripe/stripe-js';
import {
EmbeddedCheckoutProvider,
EmbeddedCheckout
} from '@stripe/react-stripe-js';
// SSR-safe component
const SSRCheckoutForm = ({ initialClientSecret = null }) => {
const [stripePromise] = useState(() =>
typeof window !== 'undefined' ? loadStripe('pk_test_...') : null
);
const [clientSecret, setClientSecret] = useState(initialClientSecret);
useEffect(() => {
if (!clientSecret && typeof window !== 'undefined') {
// Fetch client secret on client-side mount
fetchCheckoutSession();
}
}, [clientSecret]);
const fetchCheckoutSession = async () => {
try {
const response = await fetch('/api/create-checkout-session', {
method: 'POST',
});
const { client_secret } = await response.json();
setClientSecret(client_secret);
} catch (error) {
console.error('Failed to create checkout session:', error);
}
};
const fetchClientSecret = async () => {
return clientSecret || fetchCheckoutSession();
};
// Don't render checkout on server
if (typeof window === 'undefined') {
return (
<div className="checkout-loading">
<div>Loading checkout...</div>
</div>
);
}
// Don't render without Stripe or client secret
if (!stripePromise || !clientSecret) {
return (
<div className="checkout-loading">
<div>Preparing checkout...</div>
</div>
);
}
return (
<EmbeddedCheckoutProvider
stripe={stripePromise}
options={{
clientSecret,
onComplete: () => {
window.location.href = '/success';
},
onError: (error) => {
console.error('Checkout error:', error);
}
}}
>
<EmbeddedCheckout />
</EmbeddedCheckoutProvider>
);
};Custom Styling and Integration:
import React, { useState } from 'react';
import { EmbeddedCheckoutProvider, EmbeddedCheckout } from '@stripe/react-stripe-js';
const CustomizedCheckout = ({ stripePromise, clientSecret }) => {
const [isLoading, setIsLoading] = useState(true);
const [checkoutError, setCheckoutError] = useState(null);
const handleComplete = () => {
setIsLoading(false);
// Custom success handling
console.log('Checkout completed!');
// Example: Show success animation, then redirect
setTimeout(() => {
window.location.href = '/order-confirmation';
}, 2000);
};
const handleError = (error) => {
setIsLoading(false);
setCheckoutError(error.message);
// Custom error handling
console.error('Checkout failed:', error);
};
return (
<div className="custom-checkout-container">
<style jsx>{`
.custom-checkout-container {
max-width: 600px;
margin: 0 auto;
padding: 20px;
background: #ffffff;
border-radius: 12px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.checkout-header {
text-align: center;
margin-bottom: 30px;
}
.checkout-title {
font-size: 24px;
font-weight: bold;
color: #333;
margin-bottom: 8px;
}
.checkout-subtitle {
color: #666;
font-size: 16px;
}
.embedded-checkout {
min-height: 400px;
border-radius: 8px;
overflow: hidden;
}
.loading-indicator {
display: flex;
justify-content: center;
align-items: center;
height: 200px;
color: #666;
}
.error-message {
background: #fee;
border: 1px solid #fcc;
color: #c00;
padding: 12px;
border-radius: 6px;
margin-bottom: 20px;
}
`}</style>
<div className="checkout-header">
<h1 className="checkout-title">Secure Checkout</h1>
<p className="checkout-subtitle">
Complete your purchase securely with Stripe
</p>
</div>
{checkoutError && (
<div className="error-message">
<strong>Checkout Error:</strong> {checkoutError}
<button
onClick={() => window.location.reload()}
style={{ marginLeft: '10px' }}
>
Try Again
</button>
</div>
)}
<EmbeddedCheckoutProvider
stripe={stripePromise}
options={{
clientSecret,
onComplete: handleComplete,
onError: handleError
}}
>
<EmbeddedCheckout className="embedded-checkout" />
</EmbeddedCheckoutProvider>
{isLoading && (
<div className="loading-indicator">
Loading secure checkout...
</div>
)}
</div>
);
};Integration with React Router:
import React, { useEffect, useState } from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import { loadStripe } from '@stripe/stripe-js';
import { EmbeddedCheckoutProvider, EmbeddedCheckout } from '@stripe/react-stripe-js';
const stripePromise = loadStripe('pk_test_...');
const CheckoutPage = () => {
const { sessionId } = useParams();
const navigate = useNavigate();
const [clientSecret, setClientSecret] = useState(null);
const [error, setError] = useState(null);
useEffect(() => {
if (sessionId) {
// Retrieve existing checkout session
fetchExistingSession(sessionId);
} else {
// Create new checkout session
createNewSession();
}
}, [sessionId]);
const fetchExistingSession = async (sessionId) => {
try {
const response = await fetch(`/api/checkout-sessions/${sessionId}`);
const session = await response.json();
setClientSecret(session.client_secret);
} catch (err) {
setError('Failed to load checkout session');
}
};
const createNewSession = async () => {
try {
const response = await fetch('/api/create-checkout-session', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
// session parameters
}),
});
const { client_secret } = await response.json();
setClientSecret(client_secret);
} catch (err) {
setError('Failed to create checkout session');
}
};
const handleComplete = () => {
// Navigate to success page
navigate('/checkout/success');
};
const handleError = (error) => {
console.error('Checkout error:', error);
navigate('/checkout/error', { state: { error: error.message } });
};
if (error) {
return <div className="error">Error: {error}</div>;
}
if (!clientSecret) {
return <div className="loading">Loading checkout...</div>;
}
return (
<div className="checkout-page">
<EmbeddedCheckoutProvider
stripe={stripePromise}
options={{
clientSecret,
onComplete: handleComplete,
onError: handleError
}}
>
<EmbeddedCheckout />
</EmbeddedCheckoutProvider>
</div>
);
};
export default CheckoutPage;/**
* Hook to access embedded checkout context
* @returns Embedded checkout context value
*/
function useEmbeddedCheckoutContext(): EmbeddedCheckoutContextValue;
interface EmbeddedCheckoutContextValue {
/** Embedded checkout instance */
embeddedCheckout: EmbeddedCheckoutPublicInterface | null;
}
interface EmbeddedCheckoutPublicInterface {
/** Mount the checkout to a DOM element */
mount(location: string | HTMLElement): void;
/** Unmount the checkout from its current location */
unmount(): void;
/** Destroy the checkout instance */
destroy(): void;
}Custom Hook Usage Example:
import React, { useEffect, useRef } from 'react';
import { useEmbeddedCheckoutContext } from '@stripe/react-stripe-js';
const CustomCheckoutComponent = () => {
const { embeddedCheckout } = useEmbeddedCheckoutContext();
const checkoutRef = useRef(null);
useEffect(() => {
if (embeddedCheckout && checkoutRef.current) {
// Manually mount the checkout
embeddedCheckout.mount(checkoutRef.current);
return () => {
// Cleanup on unmount
if (embeddedCheckout) {
try {
embeddedCheckout.unmount();
} catch (error) {
// Handle unmount errors silently
}
}
};
}
}, [embeddedCheckout]);
return (
<div>
<h2>Custom Checkout Implementation</h2>
<div ref={checkoutRef} className="custom-checkout-mount" />
</div>
);
};The embedded checkout automatically handles most errors, but you can provide custom error handling:
const CheckoutWithErrorHandling = () => {
const [errors, setErrors] = useState([]);
const handleError = (error) => {
setErrors(prev => [...prev, {
id: Date.now(),
message: error.message,
timestamp: new Date().toISOString()
}]);
// Log to your error tracking service
console.error('Embedded checkout error:', error);
};
const clearErrors = () => setErrors([]);
return (
<div>
{errors.length > 0 && (
<div className="error-panel">
<h3>Checkout Errors</h3>
{errors.map(error => (
<div key={error.id} className="error-item">
{error.message}
<small>{error.timestamp}</small>
</div>
))}
<button onClick={clearErrors}>Clear Errors</button>
</div>
)}
<EmbeddedCheckoutProvider
stripe={stripePromise}
options={{
clientSecret,
onError: handleError
}}
>
<EmbeddedCheckout />
</EmbeddedCheckoutProvider>
</div>
);
};Install with Tessl CLI
npx tessl i tessl/npm-stripe--react-stripe-js