This members-only tutorial provides the steps to develop an Oxygen template for single pages of a "service" Custom Post Type with an attached "faq" ACF Pro Repeater field automatically appearing with expand-collapse functionality using jQuery Collapse.



Step 1

Install and activate Custom Post Type UI.

Go to CPT UI > Add/Edit Post Types.

Add service post type like so:

Click "Add Post Type" button.

Step 2

Install and activate Advanced Custom Fields Pro.

Go to Custom Fields > Tools and import this field group.

This should add a "FAQ" field group having questions_and_answers repeater field with question and answer sub fields.

Ensure that the field group is attached to the service CPT.

Step 3

Go to Services > Add New.

Add your service post type entries with Title, Content, Featured image and as many Question and Answer pairs as you want for each.

Step 4

Go to Oxygen > Templates.

Add a new template named say, "Service CPT Single".

Set it to inherit from your Main template having the site header and footer.

Apply it to Singular > Services with a priority of 1 (or more depending on how your template for sitewide single entries is setup).

Edit it with Oxygen.

Step 5

Add a Section and add your desired elements like the Post Title, Featured image and Content.

Let's loop through the questions_and_answers field and output the question and answer sub fields for each row.

Add a Code Block.

Go to Advanced > Size & Spacing and set Width to 100%.


This is a premium members-only content.

To view the rest of the content, please sign up for membership ($37/month or $399 one-time).

Already a member? Log in below or here.

Need help implementing a tutorial in your site or want to hire me for custom work?


Find the article helpful and wish to donate?



For adding code blocks wrap the code in three backticks. Markdown should work.
Provide a URL of your site/webpage if something is not working.

16 comments on “Custom Post Type with FAQs using ACF Pro Repeater Field and Expand Collapse Functionality”

  1. I am using Local By Flywheel. Trying to figure out how to do Step 7. Since it is local, all files are on my computer. Not sure where, though. Asking on Local By Flywheel, but if anyone knows Flywheel and help me out, I would appreciate it. Newbie at every step. Sigh. 🙂

  2. Ok. Found my site files. Made it, I believe, to the last instruction and it is somewhat intimidating. Here is the file. It absolutely is not laid out like below. Notepad makes it all a run-on sentence. Need a code editor, I guess. But I still do not know where to put the code for certain:

    if ( ! is_singular( ‘service’ ) ) {

    * loads JS files in the footer.
    wp_enqueue_script( 'jquery-collapse', plugin_dir_url( __FILE__ ) . 'assets/js/jquery.collapse.js', array( 'jquery' ), '1.1.2', true );

    wp_enqueue_script( 'collapse-init', plugin_dir_url( __FILE__ ) . 'assets/js/jquery.collapse.init.js', array( 'jquery-collapse' ), '1.0.0', true );

    Where do I put the code above?

    Plugin Name: My Custom Functionality
    Plugin URI:
    Description: My custom functions.
    Version: 1.0.0
    Author: Your Name
    Author URI:
    License: GPL-2.0+
    License URI:

    This plugin is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 2 of the License, or
    any later version.

    This plugin is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with This plugin. If not, see {URI to Plugin License}.

    if ( ! defined( ‘WPINC’ ) ) {

    add_action( ‘wp_enqueue_scripts’, ‘custom_enqueue_files’ );
    * Loads .
    function custom_enqueue_files() {
    // if this is not the front page, abort.
    // if ( ! is_front_page() ) {
    // return;
    // }

    // loads a CSS file in the head.
    // wp_enqueue_style( 'highlightjs-css', plugin_dir_url( __FILE__ ) . 'assets/css/style.css' );

    * loads JS files in the footer.
    // wp_enqueue_script( 'highlightjs', plugin_dir_url( __FILE__ ) . 'assets/js/highlight.pack.js', '', '9.9.0', true );

    // wp_enqueue_script( 'highlightjs-init', plugin_dir_url( __FILE__ ) . 'assets/js/highlight-init.js', '', '1.0.0', true );


  3. I’ve not been able to find an FAQ plugin that I’m happy with & want to use CPT & ACF to create a page with categories of FAQ’s.
    So I would create a ‘faqs’ CPT & add an ACF field for a drop down of FAQ Categories.

    But what I need help with that I cant get from this tutorial is the query to filter the Post Type + the ACF category field.

  4. Richard,

    Do you want to display the FAQs grouped by their categories?

    If so, do you want the expand/collapse functionality or just want to link the question to its single page that shows the question and answer?

  5. There are two ways in which you can have the content (question and answer pairs) in your WordPress site.

    1. Entries of faq custom post type.
    2. In subfields of a repeater field attached to the faq entry.

    Which do you prefer?

    With #1, you do not need ACF. This is my recommendation.

    1. You can use this sample PHP in a Code Block element:


      // check if the repeater field has rows of data.
      if ( have_rows( 'questions_and_answers', 49 ) ) {

      echo '';

      // loop through the rows of data.
      while ( have_rows( 'questions_and_answers', 49 ) ) : the_row(); ?>




      echo '</div>';

      else {
      // no rows found


      You have to replace both the instances of 49 with the ID of your CPT entry from which you want to show the repeater’s fields.

  6. Thanks, Sridhar. This worked great!

    For my implementation, I modified step 7 to serve jQuery Collapse from the jsDelivr CDN and added the script tag and init code to the Oxygen template. I did this to eliminate the need for the custom plugin.

    I’d appreciate any thoughts you have on this method.

    1. That is fine and will work as well.

      I personally prefer to use wp_enqueue_script() and wp_enqueue_style() functions as that is considered to be the standard way in which assets should be loaded in WordPress.

linkedin facebook pinterest youtube rss twitter instagram facebook-blank rss-blank linkedin-blank pinterest youtube twitter instagram
%d bloggers like this: