'Error react-stripe CardElement is not rendering?

I am integrating stripe payment on my website but the card element is not rendering here is a code

Checkout and Elements Component

import React, { useState } from 'react';
import { loadStripe } from '@stripe/stripe-js/pure';
import { Elements, CardElement, ElementsConsumer,useStripe, useElements } from '@stripe/react-stripe-js';

let stripePromise = loadStripe('pk_test_TYooMQauvdEDq54NiTphI7jx')

const CARD_OPTIONS = {
    iconStyle: "solid",
    style: {
      base: {
        iconColor: "#c4f0ff",
        color: "#fff",
        fontWeight: 500,
        fontFamily: "Roboto, Open Sans, Segoe UI, sans-serif",
        fontSize: "16px",
        fontSmoothing: "antialiased",
        ":-webkit-autofill": {
          color: "#fce883"
        },
        "::placeholder": {
          color: "#87bbfd"
        }
      },
      invalid: {
        iconColor: "#ffc7ee",
        color: "#ffc7ee"
      }
    }
  };


export const CheckoutForm = (props) => {
    console.log(props);
    const stripe = useStripe();
    const elements = useElements();
    const [cardComplete, setCardComplete] = useState(false);
    console.log(stripe, elements, 'krishna');
    console.log(CardElement);
    return (
        <form>
            <CardElement
                options={CARD_OPTIONS}
                onReady={()=>{
                    console.log("Card Element is ready")
                    
                }}
                onChange={props.onChange}
            />
            <button type="submit" disabled={!stripe}>
                Pay
            </button>
        </form>
    );
};

const InjectedCheckoutForm = () => {
    return (
      <ElementsConsumer>
        {({elements, stripe}) => (
          <CheckoutForm elements={elements} stripe={stripe} />
        )}
      </ElementsConsumer>
    );
  };
export const StripeForm = (props) => {
    console.log(stripePromise)
    return (
        <Elements stripe={stripePromise}>
            <InjectedCheckoutForm />
        </Elements>
    );
};

Component Where I am using Stripe Element

import React, { Component, useEffect, useState } from 'react';
import InformationForm from './InformationForm';
import Style from '../../styles/checkout_information.module.scss';
import { StripeForm } from './CheckoutForm';

const InputField = ({ _placeholder, _name, _label, _required, _size }) => {
    return (
        <section className={[Style.input_wrapper, _size && Style[_size]].join(' ')}>
            <label className={Style.label}>
                {_label}
                {_required && <sup className={Style.isRequired}>*</sup>}
            </label>
            <input className={Style.input} name={_name} required={_required} placeholder={_placeholder} />
        </section>
    );
};

const SelectField = ({ _placeholder, _name, _label, _required, _size }) => {
    return (
        <section className={[Style.input_wrapper, _size && Style[_size]].join(' ')}>
            <label className={Style.label}>
                {_label}
                {_required && <sup className={Style.isRequired}>*</sup>}
            </label>
            <select
                className={[Style.input, Style.select].join(' ')}
                name={_name}
                required={_required}
                placeholder={_placeholder}
            >
                <option></option>
            </select>
        </section>
    );
};

export default class PersonalDetails extends Component {
    constructor(props) {
        super(props);
    }

    componentDidMount() {
        // console.log(this.props);
    }
    render() {
        return (
            <div className={Style.details_container}>
                <InformationForm title={'Personal Details'}>
                    <fieldset className={Style.form_group}>
                        <form className={Style.form}>
                            <div className={Style.row}>
                                <InputField
                                    _type={'text'}
                                    _label={'First Name'}
                                    _required={true}
                                    _placeholder={'Jane'}
                                    _name={'firstName'}
                                    _size={'large'}
                                />
                                <InputField
                                    _type={'text'}
                                    _label={'Last Name'}
                                    _required={true}
                                    _placeholder={'Doe'}
                                    _name={'lastName'}
                                    _size={'large'}
                                />
                            </div>
                            <div className={[Style.row, Style.mt_1].join(' ')}>
                                <InputField
                                    _type={'email'}
                                    _label={'Email Address'}
                                    _required={true}
                                    _placeholder={'[email protected]'}
                                    _name={'email'}
                                    _size="large"
                                />
                                <InputField
                                    _type={'text'}
                                    _label={'Phone Number'}
                                    _required={true}
                                    _placeholder={'(941)-555-0123'}
                                    _name={'phoneNumber'}
                                    _size="large"
                                />
                            </div>
                        </form>
                    </fieldset>
                </InformationForm>
                <InformationForm margin={'mt_1'} title={'Payment Details'}>

                    <StripeForm />

                    
                </InformationForm>
                <InformationForm margin={'mt_1'} title={'Billing Details'}>
                    <fieldset className={Style.form_group}>
                        <div className={Style.row}>
                            <SelectField
                                _type={'text'}
                                _label={'Country'}
                                _required={true}
                                _placeholder={'Jane'}
                                _name={'country'}
                                _size="large"
                            />
                        </div>
                        <div className={[Style.row, Style.mt_1].join(' ')}>
                            <InputField
                                _type={'text'}
                                _label={'Street Address'}
                                _required={true}
                                _placeholder={'Address Line 1'}
                                _name={'address'}
                                _size="large"
                            />
                            <InputField
                                _type={'text'}
                                _label={'Seccondry Address'}
                                _required={false}
                                _placeholder={'Address Line 2'}
                                _name={'secondryLine'}
                                _size="large"
                            />
                        </div>
                        <div className={[Style.row, Style.mt_1].join(' ')}>
                            <InputField
                                _type={'text'}
                                _label={'city'}
                                _required={true}
                                _placeholder={'Address Line 1'}
                                _name={'address'}
                                _size="large"
                            />
                            <div className={Style.row}>
                                <SelectField
                                    _type={'text'}
                                    _label={'State'}
                                    _required={true}
                                    _placeholder={'Cincinati'}
                                    _name={'secondryLine'}
                                    _size="small"
                                />
                                <InputField
                                    _type={'text'}
                                    _label={'Zip Code'}
                                    _required={true}
                                    _placeholder={'22121'}
                                    _name={'zipCode'}
                                    _size="small"
                                />
                            </div>
                        </div>
                    </fieldset>
                </InformationForm>
            </div>
        );
    }
}

Its not showing any error on console. and also loadStripe is sending request to stripe API end but its promise is showing pending

version of sdks

"@stripe/react-stripe-js": "^1.4.0",
"@stripe/stripe-js": "^1.14.0",


Solution 1:[1]

As noted in the docs, the ElementsConsumer is meant to be used with Class-based components. Since your components are all functional, you should use the recommended pattern here:

export const StripeForm = (props) => {
    console.log(stripePromise)
    return (
        <Elements stripe={stripePromise}>
            <CheckoutForm />
        </Elements>
    );
};

And remove the InjectedCheckoutForm entirely.

Solution 2:[2]

I recently got this issue after upgrading to React 18 and having to specify async functions within useEffects instead of async directly on the useEffect callback. Or atleast I think that is when the issue occured. The problem I believe is that Stripe will attempt to render the CC input element once, if it does not have the stripe key and client secret, it will attempt to load with bad info and not attempt again, ensuring that you get instead of your stripe element. So you have to check if you have the client secret available first.

In any case, what worked for me is adding a "options" property to the Elements stripe element and defining the client_secret from server on it and rendering it only if that client secret had been defined already.

const stripePromise = loadStripe(keys.livekey);

const CreditCard = props => {
  let { stripeSecret, ...rest } = props;

  return (
    stripeSecret && stripeSecret.client_secret ?
      <Elements stripe={stripePromise} options={{ clientSecret: stripeSecret.client_secret}}>
        <ElementsConsumer>
        ...
        </ElementsConsumer>
      </Elements>
    : null
  )
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Nolan H
Solution 2