WordPress

Beautiful FAQ Structured Data Markup from ACF Fields 2023

watchLast Updated:

bookmarkWritten by: Yakovos Frountas

    FAQ Structured Data Markup is something new to our digital life. Google now can display directly in the search results Frequently asked questions about a specific topic. In this article, you will learn how you can implement this new FAQ rich results with Advanced Custom Fields in your WordPress Theme. To follow this tutorial you should have FTP Access to your site and an admin account to install Advanced Custom Fields Pro.

    [toc]

    You have to know that the following example will work only inside the Wordpress loop.

    Analyze our FAQ Structured Data setup

    Our website has an FAQ section and this will be our walk-through for this tutorial. This page was created by using ACF PRO and repeater fields. Our Fields setup Contains, where – is an inheritance indicator.

    • A parent repeater named FAQ – Repeater Field
    • A child field named FAQ Section Name – – Text Field
    • A nested repeater named FAQ Section – – Repeater Field
    • A child Text area field of nested repeater named Question – – – Text Area Field
    • A child Wysiwyg Editor field of nested repeater named Answer – – – Wysiwyg Editor

    So, our Advanced Custom Fields Field editor should look like:

    FAQ Structured Data on BakemyWP

    Let’s code those fields

    A new page template is the best idea to build our FAQ section. We strongly recommend to follow this way but you can also use other methods like page or post even custom post type but maybe some steps could be a little bit different. So the FAQ fields based on ACF should have this markup in PHP.

    <?php if (have_rows('faq')) : ?>
    
      <?php while (have_rows('faq')) : the_row(); ?>
    
        <h3 class="playfair bold extra-margin"><?php the_sub_field('faq_section_name'); ?> (<?php echo count(get_sub_field('faq_section')); ?>)</h3>
    
        <?php if (have_rows('faq_section')): ?>
    
        <ul class="accordion" data-accordion data-allow-all-closed="true">
          <?php while (have_rows('faq_section')): the_row(); ?>
            <li class="" data-accordion-item>
              <a href="#" class="accordion-title yantramanav bold"><?php the_sub_field('faq_question'); ?></a>
              <div class="accordion-content yantramanav light" data-tab-content>
              <?php the_sub_field('faq_answer'); ?>
              </div>
            </li>
          <?php endwhile; ?>
        </ul>
    
      <?php endif; ?>
    
      <?php endwhile; ?>
    
    <?php endif; ?>

    Create the FAQ Structured Data Markup

    Once our main loop has run we need to create the same loop again in order to get the fields ready for JSON encoding. Notice that we do not want our FAQ Structured Data Markup to appear inside the elements. We want it to the footer of the site for many reasons, mainly because we are going to generate a script. This time our loop should look like:

    <?php 
    
        global $schema;
        
        $schema = array(
        '@context'   => "https://schema.org",
        '@type'      => "FAQPage",
        'mainEntity' => array()
        );
    
        if ( have_rows('faq') ) {
    
        while ( have_rows('faq') ) : the_row();
    
          if ( have_rows('faq_section') ) {
    
            while ( have_rows('faq_section') ) : the_row();
    
              $questions = array(
                '@type'          => 'Question',
                'name'           => get_sub_field('faq_question'),
                'acceptedAnswer' => array(
                '@type' => "Answer",
                'text' => get_sub_field('faq_answer')
                  )
                  );
    
                  array_push($schema['mainEntity'], $questions);
    
            endwhile;
    
          }
    
        endwhile;
    
    
        function bakemywp_generate_faq_schema ($schema) {
    
          global $schema;
    
          echo '<!-- Auto generated FAQ Structured data by Bakemywp.com --><script type="application/ld+json">'. json_encode($schema) .'</script>';
    
        }
    
        add_action( 'wp_footer', 'bakemywp_generate_faq_schema', 100 );
    
    
      }
    
    ?>

    Put it all together

    At the end our FAQ Structured Data ACF loop looks awesome!

    <?php if (have_rows('faq')) : ?>
    
      <?php while (have_rows('faq')) : the_row(); ?>
    
        <h3 class="playfair bold extra-margin"><?php the_sub_field('faq_section_name'); ?> (<?php echo count(get_sub_field('faq_section')); ?>)</h3>
    
        <?php if (have_rows('faq_section')): ?>
    
        <ul class="accordion" data-accordion data-allow-all-closed="true">
          <?php while (have_rows('faq_section')): the_row(); ?>
            <li class="" data-accordion-item>
              <a href="#" class="accordion-title yantramanav bold"><?php the_sub_field('faq_question'); ?></a>
              <div class="accordion-content yantramanav light" data-tab-content>
              <?php the_sub_field('faq_answer'); ?>
              </div>
            </li>
          <?php endwhile; ?>
        </ul>
    
      <?php endif; ?>
    
      <?php endwhile; ?>
    
      <?php 
        
        global $schema;
    
        $schema = array(
        '@context'   => "https://schema.org",
        '@type'      => "FAQPage",
        'mainEntity' => array()
        );
    
        if ( have_rows('faq') ) {
    
        while ( have_rows('faq') ) : the_row();
    
          if ( have_rows('faq_section') ) {
    
            while ( have_rows('faq_section') ) : the_row();
    
              $questions = array(
                '@type'          => 'Question',
                'name'           => get_sub_field('faq_question'),
                'acceptedAnswer' => array(
                '@type' => "Answer",
                'text' => get_sub_field('faq_answer')
                  )
                  );
    
                  array_push($schema['mainEntity'], $questions);
    
            endwhile;
    
          }
    
        endwhile;
    
    
        function bakemywp_generate_faq_schema ($schema) {
    
          global $schema;
    
          echo '<!-- Auto generated FAQ Structured data by Bakemywp.com --><script type="application/ld+json">'. json_encode($schema) .'</script>';
    
        }
    
        add_action( 'wp_footer', 'bakemywp_generate_faq_schema', 100 );
    
    
      }
    
      ?>
    
    <?php endif; ?> <!-- endif have_rows('faq'); -->

    Testing our page on Google

    If everything is working fine in Google Structured Data Testing Tool your site should have no errors in search results.

    Google Structured Data testing tool for FAQ Page

    2023 August Update

    Google Changed How FAQ appears on search results

    Google made an announcement on August 8, 2023, regarding alterations in the display of FAQ and HowTo rich results. To clarify, FAQ rich results will appear less often in search result snippets, and HowTo rich results will now be exclusively available on desktop devices. Google’s rationale behind this update is to enhance the search experience by ensuring a cleaner and more uniform presentation.

    Google said that How-To (derived from HowTo structured data) rich results will exclusively be displayed to desktop users and will not be accessible to those using mobile devices. It’s important to note that, with mobile indexing, Google uses the mobile version of a website for indexing. Therefore, if you want How-To rich results to appear on desktops, you must ensure that the mobile version of your website contains the required markup.

    You can read more here.

    What we believe about FAQ structured data

    We firmly believe that incorporating HowTo schema into your website is a valuable strategy whenever feasible. This action can only bring benefits and ensures you don’t miss out on long-term advantages in the realm of Search Engine Optimization.

    39 thoughts on “Beautiful FAQ Structured Data Markup from ACF Fields 2023”

    1. Hi,

      this is exactly what I was needing, but…

      the line 3 ()

      throws an error (PHP 7.3.):

      PHP Warning: count(): Parameter must be an array or an object that implements Countable

      What could it be and how to resolve it?

      Reply
    2. Never mind, I solved it. You can delete those two comments. 😉

      Thx for this wonderful script. Very helpful in my case.

      Oliver

      Reply
    3. I’ve had issues using this. When I’ve check the source the code gets up to main entity but says $null and no other information is displayed. But the FAQ displays correct on the site?

      Reply
      • Hey Ryan! If you getting this error probably you have made something wrong with the sub fields of the repeater. Can you please send us an email to hello[at]bakemywp.com the URL of the page you are seeing this error in order to assist you further? Thanks!

        Reply
    4. I am getting this as a result – followed the guide step by step

      {“mainEntity”:null}

      Any ideas?

      Reply
      • Hey Kyle! Probably you are doing something wrong in this step array_push($schema[‘mainEntity’], $questions); If you need further help please contact us at hello[at]bakemywp.com

        Reply
        • Not sure how emailing will help as I am your code exactly as it is on this page. Field names in ACF match as well as I get the front end showing ok its just the Schema that doesn’t work.

          Reply
          • Emailing to us will help to find what causes the empty array. As you can see in this page we follow exact the same code and its working fine in Structured Data Testing Tool.

            Reply
      • For all having this problem with {“mainEntity”:null} – In the code shown in the tutorial global $schema has to be put BEFORE $schema array definition:

        global $schema;
        $schema = array(
        ‘@context’ => “https://schema.org”,
        ‘@type’ => “FAQPage”,
        ‘mainEntity’ => array()
        );

        Reply
        • Thank you for this however I still get the error “A value for the mainEntity field is required.”

          My code:

          global $schema;
          $schema = array(
          ‘@context’ => “https://schema.org”,
          ‘@type’ => “FAQPage”,
          ‘mainEntity’ => array()
          );
          if (have_rows(‘section_accordion’)) {
          while ( have_rows(‘section_accordion’) ) : the_row();

          $questions = array(
          ‘@type’ => ‘Question’,
          ‘name’ => get_sub_field(‘section_accordion_item_title’),
          ‘acceptedAnswer’ => array(
          ‘@type’ => “Answer”,
          ‘text’ => get_sub_field(‘section_accordion_item_content’)
          )
          );
          array_push($schema[‘mainEntity’], $questions);
          endwhile;
          }
          function bakemywp_generate_faq_schema($schema) {
          global $schema;
          echo ‘‘ . json_encode($schema) . ”;
          }
          add_action(‘wp_footer’, ‘bakemywp_generate_faq_schema’, 100);

          Reply
    5. Hello, I been trying to convert this FAQ style to a Local Business scheme but I couldn’t get it to work. Could you please help me to achieve this.

      Reply
      • Hello Maksut! This code is only for FAQ. It can not be converted to Local Business because Local Business is something different. We can do this for you as a task. In order to make a request please visit our Free Quote page and submit a new ticket there! Thanks!

        Reply
    6. Hi

      Thank you for this template.

      However, if I want to set this up inside an ACF custom block, how would that look?

      Using the code above in a block returns the errors “Warning: array_push() expects parameter 1 to be array” and “cannot redeclare the function twice” (the function that outputs the Schema)

      Thank you.

      Reply
    7. I want to hook this code into Header inside head tag, except for footer so what should be the code will?
      i have applied this but it is not working
      add_action( ‘wp_head’, ‘bakemywp_generate_faq_schema’, 100 );
      thanks in advance

      Reply
      • That’s a fair question but this tutorial is more focused on the Schema Markup and not in the HTML & CSS part. If you search the Google about the term accordion you will find really nice examples to build your own.

        Reply
      • Hi Pavel, if you need to create a HowTo markup with ACF Blocks or just with ACF Fields please feel free to contact us we offer services for custom block development or plugin development in affordable prices.

        Thank you 😊

        Reply
      • Hello, this code isn’t for use in the validator tool you have to create the fields from within the ACF plugin and put this code in your functions.php file. Then you can create some content in order to verify your code. You can check this page https://bakemywp.com/faqs/ has exactly that code to generate the schema markup.

        Reply
        • when I use this code getting an error can u check it for me, please Thank you

          /* Style the buttons that are used to open and close the accordion panel */
          .accordion {
          background-color: #eee;
          color: #444;
          cursor: pointer;
          padding: 18px;
          width: 100%;
          text-align: left;
          border: none;
          outline: none;
          transition: 0.4s;
          margin-bottom: 10px;
          border: none;
          }

          /* Add a background color to the button if it is clicked on (add the .active class with JS), and when you move the mouse over it (hover) */
          .active, .accordion:hover,
          button:focus {
          background-color: #ccc;
          background: #ccc;
          border: none;
          }

          /* Style the accordion panel. Note: hidden by default */
          .panel {
          padding: 0 18px;
          background-color: white;
          display: none;
          overflow: hidden;

          }

          .accordion:after {
          content: ‘\02795’; /* Unicode character for “plus” sign (+) */
          font-size: 13px;
          color: #777;
          float: right;
          margin-left: 5px;
          }

          .active:after {
          content: “\2796”; /* Unicode character for “minus” sign (-) */
          }

          var acc = document.getElementsByClassName(“accordion”);
          var i;

          for (i = 0; i < acc.length; i++) {
          acc[i].addEventListener("click", function() {
          /* Toggle between adding and removing the "active" class,
          to highlight the button that controls the panel */
          this.classList.toggle("active");

          /* Toggle between hiding and showing the active panel */
          var panel = this.nextElementSibling;
          if (panel.style.display === "block") {
          panel.style.display = "none";
          } else {
          panel.style.display = "block";
          }
          });
          }

          “https://schema.org”,
          ‘@type’ => “FAQPage”,
          ‘mainEntity’ => array()
          );

          if ( have_rows(‘faq_section’) ) {

          while ( have_rows(‘faq_section’) ) : the_row();

          $questions = array(
          ‘@type’ => ‘Question’,
          ‘name’ => get_sub_field(‘faq_question’),
          ‘acceptedAnswer’ => array(
          ‘@type’ => “Answer”,
          ‘text’ => get_sub_field(‘faq_answer’)
          )
          );

          array_push($schema[‘mainEntity’], $questions);

          endwhile;

          function bakemywp_generate_faq_schema ($schema) {

          global $schema;

          echo ‘‘. json_encode($schema) .”;

          }

          add_action( ‘wp_footer’, ‘bakemywp_generate_faq_schema’, 100 );

          }

          ?>

          <?php
          endwhile; //End the loop // no rows found

          else:
          echo "Come back tomorrow";
          endif;

          Reply

    Leave a Comment

    Ready to get started?

    Registering for a maintenance plan ensures that your website will be regularly updated and secure, preventing any potential issues or hacks,