next.jsstripe-payments

Nextjs Stripe elements not possible customize layout


I fallowing to documentation library:

https://github.com/stripe/react-stripe-js

I try set different layout from horizontal to vertical tabs

This part of code working:

    const appearance = {
        theme: "stripe",  
    };


When i switch theme then i see rendering new theme. But when i try pass options layout like this:

    const options = {
        clientSecret,
        appearance,
        layout: {
            type: 'accordion',         
            defaultCollapsed: false,    
            radios: true,             
            spacedAccordionItems: true,  
        },
    };

Then not working, not changes in fronted.

Here is full code example

'use client';

import { useState, useEffect } from "react";
import { loadStripe } from "@stripe/stripe-js";
import { Elements, PaymentElement, useStripe, useElements } from "@stripe/react-stripe-js";

// Load your Stripe public key
const stripePromise = loadStripe("pk_test ");

export default function PaymentPage() {
    const [clientSecret, setClientSecret] = useState("");

    useEffect(() => {
        fetch("/api/create-payment-intent", {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify({ amount: 5000, currency: "pln" }), // Adjust amount & currency
        })
            .then((res) => res.json())
            .then((data) => setClientSecret(data.clientSecret));
    }, []);

    // Appearance and layout configuration
    const appearance = {
        theme: "stripe",  
    };

    // Custom layout options for Accordion  
    const options = {
        clientSecret,
        appearance,
        layout: {
            type: 'accordion',         
            defaultCollapsed: false,    
            radios: true,             
            spacedAccordionItems: true,  
        },
    };

    return (
        <div className="flex flex-col items-center gap-4 p-6">
            {clientSecret ? (
                <Elements stripe={stripePromise} options={options}>
                    <CheckoutForm />
                </Elements>
            ) : (
                <p>Loading payment options...</p>
            )}
        </div>
    );
}

function CheckoutForm() {
    const stripe = useStripe();
    const elements = useElements();
    const [loading, setLoading] = useState(false);
    const [error, setError] = useState("");

    const handleSubmit = async (event) => {
        event.preventDefault();
        if (!stripe || !elements) return;

        setLoading(true);

        const { error } = await stripe.confirmPayment({
            elements,
            confirmParams: { return_url: `${process.env.NEXT_PUBLIC_FRONTEND_URL}/success` },
            redirect: "if_required",
        });

        if (error) {
            setError(error.message);
            setLoading(false);
        } else {
            alert("Payment successful!");
        }
    };

    return (
        <form onSubmit={handleSubmit} className="w-full max-w-full">
            <PaymentElement />
            {error && <p className="text-red-500">{error}</p>}
            <button
                type="submit"
                disabled={!stripe || loading}
                className="px-4 py-2 bg-blue-500 text-white rounded"
            >
                {loading ? "Processing..." : "Pay Now"}
            </button>
        </form>
    );
}

Solution

  • The layout options which you've defined should be passed into the PaymentElement component instead.

    Example

    const paymentElementOptions = {
        layout: {
        type: 'accordion',         
        defaultCollapsed: false,    
        radios: true,             
        spacedAccordionItems: true,  
      }
    };
    
    return (
       <PaymentElement id="payment-element" options={paymentElementOptions} />
    );
    

    You can refer to the quickstart guide and refer to the JS doc for more details.