I'm trying to wrap 'some' of the fields of the checkout page in a div and inject some buttons because I am implementing some address validation.

Basically to start with I need a wrapper around company, address line 1, address line 2, town and postcode so I can show/hide them but I can't seem to get anything to work. Here's what I've tried -

In my filters file:

function change_woocommerce_field_markup($field, $key, $args, $value){
    if($key === 'billing_company') {
        $field = '<div class="address-wrapper">'.$field;
    else if ($key === 'billing_postcode') {
        $field = $field.'</div>';
    else {
        $field = $field;
    return $field;
add_filter('woocommerce_form_field', '\App\change_woocommerce_field_markup', 10, 4);

So I'm prepending and appending the relevant fields with the html that opens and closes the wrapper. However the resultant HTML looks like this:

<div class="address-wrapper"></div> <p class="form-row form-row-first validate-required" id="billing_first_name_field".... <p class="form-row form-row-last validate-required" id="billing_last_name_field"....

So it's putting the wrapper at the top of the container! I don't get it, what's going on with this?

Thanks Kevin

2 Answers 2


In your case, you don't need to use the filter to inject the wrapper. Just override the checkout template file and put the wrapper at the line you need to. In your question, you want to modify the billing form. So, it should be from this file woocommerce/templates/checkout/form-billing.php. You can inject your wrapper at this line.

If you don't know how to override the WooCommerce template. Please visit this link: https://docs.woocommerce.com/document/template-structure/

UPDATE: code sample.

foreach ( $fields as $key => $field ) {
    $field['return'] = true;
    $before = "";
    $after = "";

    switch ($key) {
        case 'billing_company':
            $before = '<div class="address-wrapper">';
        case 'billing_postcode':
            $after = '</div>';
            # code...

    echo $before . woocommerce_form_field( $key, $field, $checkout->get_value( $key ) ) . $after;
  • All the fields are outputed by the woocommerce_form_field function though, but I don't want to wrap all of the fields, just a few of them - I don't think your solution will do that will it?
    – the_lar
    Sep 27, 2019 at 16:04
  • you can do the check condition at the checkout template on foreach loop. Sep 27, 2019 at 16:44
  • @the_lar, I also update the code sample on the answer for you. Sep 27, 2019 at 16:53
  • Thank you for the code, I really appreciate you trying to help me! I tried it out and I added an inline style to the wrapper so I could see it $before = '<div class="address-wrapper" style="background: red;">'; - so what is happening is that I'm getting a flash of red around the fields, then it gets removed and address-wrapper div is moved up above all the billing fields. There must be some dom manipulation going on?
    – the_lar
    Sep 28, 2019 at 7:09
  • Hi, after quite a lot of detective work I've tracked this down to the javascript which is in woocommerce/assets/js/frontend/address_i18n.js - this is manipulating the dom in the country_to_state_changing, specifically line 143 where it is detaching the input rows and appending them back to the wrapper! So now I know where this is happening, how would you suggest I alter this behaviour? Obviously I want to do it in a way that won't break when I update woocommerce?
    – the_lar
    Sep 28, 2019 at 17:44

I am using Bootstrap 4.x. So I wanted to change the structure too. What I have done is the following:

In woocommerce/templates/checkout/form-billing.php I can target all fields separately, by calling:

<div class="row">
    <div class="col-6">
       <?php woocommerce_form_field('billing_first_name', array('placeholder' => 'Vorname *', 'required' => true), $checkout->get_value('billing_first_name')); ?>
    <div class="col-6">
       <?php woocommerce_form_field('billing_last_name', array('placeholder' => 'Nachname *', 'required' => true), $checkout->get_value('billing_last_name')); ?>

The output is inserted inside of <p class="form-row form-row-first validate-required" id="billing_<name-of-field>_field">, because it is predefined in: plugins/woocommerce/includes/wc-template-functions.php on line 2692. So there is no way to alter that, because it is hardcoded in the template function. You can write your own form field in plain HTML instead, check the HTML-Output of woocommerce_form_field();

@the_lar: Thank you for your information: Thank you for pointing out that there is a problem with the country field, I've run into this annoying bug. If you check the file, there is on line 141 the following code: rows.detach().appendTo( wrapper );. If you check line 114 you will see that rows points to .form-row, which is hardcoded in wc-template-functions.php.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.