A Modular Approach to WordPress Theming, Using ACF Flexible Content

Last Updated June 23, 2016

I do a lot of WordPress Development. One of the things I’ve started doing is coding custom themes so they’re modular. What does this mean?

It means that I focus on each of the elements within the theme as a separate element and allow those pieces to be rearranged in a way that suits the client’s needs. Some things are better demonstrated with an example:

One of the sites I’ve built is billseaver.com.

Bill Seaver's Website

On the home page, each “stripe” is considered a module. This gives Bill the freedom to rearrange the elements on the home page on the fly. If he has a podcast release that day, he can move the podcast stripe to the top, increasing its visibility.

ACF Flexible Content Demo

As a designer, I don’t feel like the design of the site is being compromised because I’ve designed how each section should look. It’s just providing more flexibility and more value to my customers, accommodating their digital strategy.

So, how did I pull this off?

The trick is the Advanced Custom Fields (ACF) plugin. — It’s one of those WordPress Plug Ins I could not live without.

Let’s walk through the setup.

Within WordPress

I have the ACF Pro Plugin installed. Once it’s activated, it gives you a Custom Fields nav item in the left sidebar. Click on that and then the Add a New Field button.

New field in ACF

Let’s call this field group “Home Page” but you can really call it anything you like. This label will be displayed on the “Edit Home Page” above our form fields.

On the new Field Group screen:
ACF Field Group Name

How the field will be displayed on the Edit Home Page screen:
Naming Flexible Content

Before we start adding fields, let’s adjust our Location rules. I want this field group to display on the home page only. Meaning, Bill will have to click on Pages and then find the Home page to edit. There are several ways I could set the rules.

I could say if the Page is equal to Home.

ACF - Options for location page

or I could say if the Page Template is equal to Home.

ACF Options, location on the page template

Personally, I prefer the second option since the template itself is really dependent on these fields.

Then, in the Options box, below that, we can control the display and the other elements on the page. I try and hide all the elements I don’t need to minimize confusion:

  • Content Editor (all content is within the stripes, no need for body copy, which means no need for an Excerpt)
  • Excerpt
  • Comments
  • Author (don’t need to set an author for the home page)
  • Featured Image
  • Send Trackbacks
  • Custom Fields (this refers to the WordPress built in Custom Fields, not the Advanced Custom Fields we’re creating)
  • Format
  • Categories
  • Discussion
  • Tags

Options within the ACF Field Plugin

Now for the fun part, the actual fields. Click the Add Field button. I’m going to name this “Home Page Content”, but call it whatever you like.

Flexible Content field on the Page Template


If you’re looking at my screenshots, I have a few other fields on the home page (background, subheading, and tertiary heading), in addition to the flexible content fields we’re creating. I don’t go into details about those fields, here, since our focus is on the flexible content.

You can see that the Field Name gets filled in automatically based on how you name the label field. I usually keep the default name unless it adds in dashes (By default, “Home – Page” will become “home_-_page”, so I’ll remove the “-“, changing it to “home_page”).

Select Flexible Content from the Field Type dropdown menu.

You can add instructions if you’d like. I usually leave this blank unless it’s a link (I’ll remind people to include the http://) or an image (I’ll include the image dimensions).

Within the Layout Row let’s start naming our stripes. In this case, we have 5.

Let’s name the first one “Receive Email Updates.” We don’t need any additional fields here since it’s simply a MailChimp Signup Form.

Click on the Add New link under Layout to add another row.

ACF - Add new row

Name this one “New Podcast”. It will automatically pull in the most recent podcast. The only additional fields we might need is a background image. Click on the Add Field button. Name it (Podcast Background Image) and select Image from the Field Type dropdown menu. In this particular case, I’m going to select the Image URL radio button, but the Image Array option is useful when you want to grab a particular size and include the alt text.

Podcast Background Image within ACF

Click on the Add New link under Layout to add another row. You get the idea. I went through the same process for the “Testimonials” (no input fields), “From the Blog” (no input fields), and the “Services” stripe with the following input fields:

  • Column 1 Heading
  • Column 1 Content
  • Column 2 Heading
  • Column 2 Content
  • Column 3 Heading
  • Column 3 Content

Within the Code

Within my WordPress theme folder, I have a subfolder called partials. This includes smaller elements that I can reuse in other places on the site. In the case of Bill’s site, that folder contains a file for every stripe:

  • stripe-email.php
  • stripe-podcast.php
  • stripe-services.php
  • stripe-testimonial.php
  • blog-excerpt.php

You’ll see the folder structure in the left pane and the code for the services stripe on the right:

Wordpress Theme Folder Structure


You’ll notice the “From the Blog” stripe is not prepended with “stripe”, that’s because that element is not only used on the home page, it’s also used to display a blog excerpt on the archives, categories, and tags pages.

My page-home.php template has this code. I tried to include comments, explaining what each section does:

<main class="main" role="main">
<?php // open the WordPress loop
if (have_posts()) : while (have_posts()) : the_post();

	// are there any rows within within our flexible content?
	if( have_rows('home_page_content') ): 

		// loop through all the rows of flexible content
		while ( have_rows('home_page_content') ) : the_row();

		if( get_row_layout() == 'receive_email_updates' )
			get_template_part('partials/stripe', 'email');

		if( get_row_layout() == 'new_podcast' )
			get_template_part('partials/stripe', 'podcast');

		if( get_row_layout() == 'services' )
			get_template_part('partials/stripe', 'services');

		if( get_row_layout() == 'testimonials' )
			get_template_part('partials/stripe', 'testimonial');

		if( get_row_layout() == 'from_the_blog' )
			get_template_part('partials/blog', 'excerpt');

		endwhile; // close the loop of flexible content
	endif; // close flexible content conditional

endwhile; endif; // close the WordPress loop ?>


You’ll notice that my if statements do not have brackets ({ and }). They’re not needed since there’s only one line of code after the condition. However, if you need to add additional lines within the statement, be sure to insert those brackets!

If you see the have_rows() and get_row_layout() functions, that’s all ACF. You can get additional documentation for the Flexible Content fields on ACF’s site.

The get_template_part() function is built into WordPress (more information within the WordPress Codex). The first parameter of the function is the folder name (partials) and the first part of the file name before the dash (remember I named my files stripe-email.php?). The second parameter is the file name after the dash (email).

The get_template_part() function is great for other places in your WordPress themes, not just within Flexible Content. I mentioned the blog-excerpt.php snippet is also used within the archive.php template:

<?php if (have_posts()): while (have_posts()) : the_post(); ?>
	<!-- article -->
	<article id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
		<?php get_template_part('partials/blog', 'post'); ?> 
	<!-- /article -->
<?php endwhile; ?>

The beauty in this method is if I need to change the way the blog excerpt is being displayed, I only have to change one file! I don’t have to remember every place that a blog excerpt is being implemented.

Other implementations

This modular method is also useful for formatting content. I used it on the portfolio section of my site, amyhaywood.com.

I knew each page would need a different format depending on whether it was a website or a print project and what assets I have for each.

ACF Flexible content applied to Jimmy Needham Splash Page

ACF Flexible Content applied to Small Groups Handbook on amyhaywood.com

Just to get your juices flowing…

There are other fields within the ACF plugin that you can use in combination with the Flexible Content field. A few examples:

  • Gallery Field Great for featuring a photo gallery, if the client has had a recent event.
  • Relationship Field Works well for pulling in specific posts to feature, giving the user the ability to pick a certain post to feature instead of using a category, tag, or most recent post.
  • Repeater I’ve used this on a number of “stripes”: Grid of logos, grid of icons with text, just to name a couple.
  • Gravity Form If you’re feeling especially fancy, there’s an additional plugin that will allow you to select one (or more) Gravity Forms.

As you can see, the possibilities are almost endless! What are some ways you’ve made use of flexible content?

The Conversation

  • Jeremy Carlson

    Amy, this is super-helpful. I’ve been eyeing the ACF Pro plugin, and this helped me get my head around how flexible content could … well, rock my world! Thanks!

    • Amy Dutton

      Awesome! And thanks for the comment. Let me know if there’s anything I can help you with once you start digging in.

      • Jeremy Carlson

        Will do! I just bought it, playing now. Having fun. Was experimenting w/ a page builder plugin recently. This feels so much quicker and somehow more stable. Loving it.

  • Hey Amy, you can cut down your page-home.php even further, if you keep the flexible content sections named the same as the file, e.g.
    if( have_rows( ‘page_builder’ ) ) {
    while ( have_rows( ‘page_builder’ ) ) {
    get_template_part( ‘templates/page-builder’, get_row_layout() );

    • Amy Dutton

      Brilliant I’ll update the post to reflect your recommendation.

  • Chris Ward

    This is a great post Amy. I have a new theme build coming up and this is exactly the approach I wanted to use. Your post is going to speed it up for me a lot!

    • Amy Dutton

      That’s great to hear, Chris! Thanks!! — Don’t hesitate to reach out if you run into any roadblocks along the way. I’d love to help out.

      • Chris Ward

        Thanks Amy. Actually after posting this comment a question did come to me: have you measured or had any issues with the number of queries ACF makes to the database? I have wondered if using repeater fields and flexible content fields to create ‘drag and drop’ pages could create a lot of queries and slow the site down?

        • Amy Dutton

          I’ve never had any trouble. But, I would assume it also depends on how long your page is and how complicated your queries are.

          Either way, it never huts to turn on your page cache. My favorite plugin, right now, for caching is WP Rocket: https://wp-rocket.me/

          • Chris Ward

            Same here! Have seen great improvements on the site that I have installed it on so far.

  • Nicole Guzman

    Thank you so much for this! I have a complicated sidebar and this made everything super easy, even with flexible content and relationship fields, repeater fields, etc!

    • Amy Dutton

      Awesome! Glad to hear it!

    • Amy Dutton

      One thing you may want to consider is creating a custom post type for your sidebar. Then, building the sidebar “posts” with Flexible Content. Within individual pages, you can set up a relationship to point to a specific sidebar. — This way you’re not having to set up the same sidebar in multiple pages and can make updates in one place. Just a thought.

  • Bob Jones

    Great post Amy. How would this affect post content if a user changed theme from the original? e,g. is this approach usable for themes that would be developed and sold or would it only be useful for bespoke websites for companies that aren’t likely to change theme when they’re bored.

    • Amy Dutton

      Good question. There are several factors at play:

      1 // How did you set up your custom post types? This is solely a content issue.
      You can either set up your custom post types as part of your functions.php or as a separate plugin. If it’s in a separate plugin, then you don’t have to worry about that information “disappearing” if you change themes. Why does this matter? You can tie your ACF field display to a custom post type.

      2 // ACF itself is a plugin. This means the content is available outside of the theme. But, how you choose to display ACF’s content on the front end is determined through the actual theme files.

      If you’re selling the theme, the user would need to have the ACF files / plugin installed on their site. You’d also need to follow the guidelines on the ACF site regarding redistribution: https://www.advancedcustomfields.com/resources/including-acf-in-a-plugin-theme/

      If you’re trying to think through other options: you could do something similar with a page builder plugin, say Beaver Builder, which hooks into the_content(); tag, so technically, it wouldn’t matter what theme you’re running. — however, any tweaks established through a custom stylesheet, would also be lost when changing themes and as the designer / developer you wouldn’t be able to lock down the fields / display as easily. Although, Beaver Themer (https://www.wpbeaverbuilder.com/beaver-themer/) looks promising.

      Does that answer your question or just create more?

      • Bob Jones

        Thanks Amy. I haven’t done it yet – just in the process of creating a theme for myself that I actually thought I’d build out more and potentially sell. I think adding to functions.php would be the way to go so it’s all contained and more bullet proof.

        I’ll still def use ACF for the control and cleanliness of code I get over builders.

        • Amy Dutton

          Awesome! — and I hear you on Page Builders!!

          One more resource I’ll throw out there is Carbon Fields (https://carbonfields.net/) as an alternative to ACF. I’ve been wanting to experiment with it, but I have a developer license for ACF, it’s well supported, and has a great community around it, so I haven’t done anything with Carbon Fields yet.

  • Gleb Makarov

    Flexible Content is awesome feature of ACF, but it lacks a template saving ability feature, I developed the plugin to cover this, could be interesting for some: https://github.com/infazz/flexible-templates

    • Amy Dutton

      @glebmakarov:disqus Awesome! Thanks for sharing! I’m excited to take a look at your plugin.

    • powerbuoy

      Hmm, perhaps it’s only available in ACF Pro but it definitely does have an “Export Fields” feature. You can export as either JSON (for importing) or PHP-code (for use within the theme). Personally I register all my fields with PHP these days. So much better to keep that stuff version controlled – not to mention I re-use fields all the time.

      • Amy Dutton

        I’ve also used this plugin: https://github.com/BeAPI/ACF-PHP-Recovery when I’ve registered the fields within PHP using a different WordPress install and need to be able to modify the copied fields within WordPress.

  • powerbuoy

    Been using this approach myself the last few sites as well. Really helps to keep the admin interface tidy too since unused modules don’t even show up in the admin. I register all my fields with PHP as well, and keep them all in separate files (with separate templates/view) for easy re-use between projects. I’m starting to build up quite the library of fields like Instagram, Google Map, Slideshows etc etc. Super handy and clients seem to like the flexibility it brings too.

    • Amy Dutton

      Awesome! Glad to hear that feedback!

  • Davros Web Design

    Great article! I am starting to use ACF to give my customers a little more flexibility. I was looking for a little inspiration. Thanks!

    • Amy Dutton

      Awesome! Great to hear.

  • dpc

    can you add a row for post objects?

    • Amy Dutton

      Sure can! I’ve even used the post objects or the relationships functionality to create a “Related Posts” section.

  • Joe Read

    This is a great explanation – just what I was looking for to create some repeatable, customisable content blocks. Thank you

    • Amy Dutton


  • Thanks for the thorough explanation. I was looking for a good reference on how to implement ACF Flexible Content into my theme and I’ve found it!

    • Amy Dutton

      @clay_teller:disqus Awesome! Great to hear!

  • Great explanation! Thank you Amy. I think the question we need to ask now: What about the performance? 🙂

    • @ofyalcin:disqus I’ve never noticed a dip in performance, so I’ve never felt a need to run tests to determine exactly how speed has been impacted. — but, I think that tells you something right there.

      • Thanks for quick response Amy. I am working on a WordPress theme. I will test this and give you information about performance.

  • Thank you so much for this.Great work

  • tex88

    Great post Amy. Any thoughts on how this strategy – and ACF / Flexible Content – are impacted by Gutenberg Blocks? I have not dug into it myself yet, but they seem similar in concept.

    • Amy Dutton

      @tex88:disqus I haven’t dug a ton into Gutenberg Blocks. — And honestly, the sites that I’ve built I’ve disabled Gutenberg.

      One advantage (I think) you have with ACF / Flexible Content is that you have more control over the fields and the way they’re displayed. You might be able to get there with Gutenberg Blocks, but I believe it would require React Knowledge and writing custom blocks.

      CSS Tricks has a series on Gutenberg that I want to dive into: https://css-tricks.com/learning-gutenberg-1-series-intro/