How to load WooCommerce checkout form into a Bootstrap modal via AJAX in WordPress?

I’m building a one-page WordPress site using WooCommerce for e-commerce functionality. I have a shopping cart that opens in a Bootstrap modal. When a user clicks “Proceed to Checkout,” I want to load the WooCommerce checkout form into the same modal via AJAX, instead of redirecting to a separate checkout page.

What I’ve Done:

1. Shopping Cart Template (shopping-bag.php):

<div class="modal-header">
    <h4 class="modal-title" id="cartModalLabel">Shopping Cart</h4>
    <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
    <div id="cartContent">
        <?php foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
            $_product   = $cart_item['data'];
            // Display cart items
        } ?>
    </div>
    <div id="checkoutContent" style="display: none;">
        <!-- Checkout form will be loaded here -->
    </div>
</div>
<div class="modal-footer">
    <button type="button" class="btn btn-light" data-bs-dismiss="modal">Close</button>
    <button type="button" id="proceedToCheckoutButton" class="btn btn-primary" onclick="proceedToCheckout()">Proceed to Checkout</button>
    <button type="button" id="goBackToCartButton" class="btn btn-secondary" style="display: none;" onclick="goBackToCart()">Go Back</button>
</div>

2. JavaScript Function to Load Checkout via AJAX:

function proceedToCheckout() {
    fetch(woocommerce_params.ajax_url, {
        method: 'POST',
        headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
        body: new URLSearchParams({ action: 'load_checkout_form' })
    })
    .then(response => response.json())
    .then(data => {
        if (data.success) {
            // Hide cart content and show checkout form
            document.getElementById('cartContent').style.display = 'none';
            document.getElementById('checkoutContent').innerHTML = data.data.html;
            document.getElementById('checkoutContent').style.display = 'block';

            // Initialize WooCommerce checkout scripts
            initializeCheckoutScripts();

            // Toggle buttons
            document.getElementById('proceedToCheckoutButton').style.display = 'none';
            document.getElementById('goBackToCartButton').style.display = 'block';
        } else {
            console.error('Error loading checkout form:', data);
        }
    })
    .catch(error => console.error('AJAX error:', error));
}

function goBackToCart() {
    // Show cart content and hide checkout form
    document.getElementById('cartContent').style.display = 'block';
    document.getElementById('checkoutContent').style.display = 'none';

    // Toggle buttons
    document.getElementById('proceedToCheckoutButton').style.display = 'block';
    document.getElementById('goBackToCartButton').style.display = 'none';
}

function initializeCheckoutScripts() {
    if (typeof jQuery !== 'undefined' && typeof wc_checkout_params !== 'undefined') {
        jQuery('body').trigger('update_checkout');
        jQuery('form.checkout').wc_checkout_form();
    } else {
        console.error('WooCommerce checkout scripts are not available.');
    }
}

3. PHP Function to Handle AJAX Request (functions.php):

function load_checkout_form() {
    ob_start();
    echo do_shortcode('[woocommerce_checkout]');
    $html = ob_get_clean();
    wp_send_json_success(['html' => $html]);
    wp_die();
}
add_action('wp_ajax_load_checkout_form', 'load_checkout_form');
add_action('wp_ajax_nopriv_load_checkout_form', 'load_checkout_form');

4. Enqueue WooCommerce Scripts and Styles (functions.php):

function enqueue_woocommerce_scripts() {
    // Enqueue WooCommerce styles
    wp_enqueue_style('woocommerce-general');
    wp_enqueue_style('woocommerce-layout');
    wp_enqueue_style('woocommerce-smallscreen');

    // Enqueue WooCommerce scripts
    wp_enqueue_script('wc-checkout');
}
add_action('wp_enqueue_scripts', 'enqueue_woocommerce_scripts');

Problem:

  • When I click “Proceed to Checkout,” the modal remains empty or shows the raw HTML structure of the checkout blocks without rendering the form.
  • The checkout form doesn’t function correctly, and necessary scripts don’t seem to initialize.
  • window.wc or window.wc.checkout is undefined, so I can’t initialize the checkout scripts properly.

What I’ve Tried:

  • Ensured WooCommerce scripts and styles are enqueued.
  • Attempted to initialize the checkout scripts after loading the form.
  • Checked for JavaScript errors; none are present.

Additional Information:

  • Using the latest version of WooCommerce.
  • Custom one-page theme.
  • Modal implemented with Bootstrap 5.
  • Prefer not to use jQuery, but WooCommerce scripts depend on it.

Question:

How can I properly load the WooCommerce checkout form into a modal via AJAX, ensuring all necessary scripts are initialized so the form renders and functions correctly?

Is there a recommended way to achieve this, or are there limitations with loading the WooCommerce checkout form via AJAX into a modal?

Any help would be appreciated.