Building Component-Based WordPress Sites

September 20, 2018

Components can be thought of as discrete, reusable building blocks that make up larger design systems for the web.

Because they are developed with modularity in mind, editing or even removing a component will have a low impact on the rest of the system. This makes our WordPress sites easier to test since we’ve developed each component independently of one another — and also makes parallel development simpler because code is cleanly separated by component.

A Simple Component Example

Pictured below is an example folder structure for a single component:
All dependencies of a component, including styles, scripts, and images, are organized together to achieve the level of modularity we want. We’ve also incorporated the twig templating library via the Timber plugin for WordPress. Twig allows us to write template code that is mostly free of data manipulation or complex logic. This basic separation of concerns makes our code easier to read and maintain.

Image is a required field.

Advanced Custom Fields

WordPress theme developers will be familiar with the popular plugin Advanced Custom Fields Pro (ACF), which provides an API for creating custom data fields for any content type. By registering ACF fields via PHP rather than using the UI builder for creating fields groups, we are able to store a site’s field structures in code rather than the database. In doing so, we gain the ability to track configuration in version control and keep sites in sync easily across multiple environments.

In the following example, we’re creating a Quote Block component with fields for the quote text, quote attribution, and a thumbnail image of the quote author. Similar to this example, each component will typically be composed of a single field group. Here is what our Quote Block component would look like in the ACF UI:

Image is a required field.

This method, however, is much slower and saves our field group settings in the database rather than in code. Below is our example component field group represented in JSON format.

{
  "layout": {
    "name": "blockQuote",
    "label": "Quote Block",
    "sub_fields": [
         {
             "label": "Quote Text",
             "name": "quote_text",
             "type": "textarea",
             "instructions": "",
             "required": 0,
             "conditional_logic": 0,
             "wrapper": {
                 "width": "",
                 "class": "",
                 "id": ""
             },
             "default_value": "",
             "placeholder": "",
             "maxlength": "",
             "rows": "",
             "new_lines": ""
         },
         {
             "label": "Quote Attribution",
             "name": "quote_attribution",
             "type": "text",
             "instructions": "",
             "required": 0,
             "conditional_logic": 0,
             "wrapper": {
                 "width": "",
                 "class": "",
                 "id": ""
             },
             "default_value": "",
             "placeholder": "",
             "prepend": "",
             "append": "",
             "maxlength": ""
         },
         {
             "label": "Quote Thumbnail",
             "name": "quote_thumbnail",
             "type": "image",
             "instructions": "",
             "required": 0,
             "conditional_logic": 0,
             "wrapper": {
                 "width": "",
                 "class": "",
                 "id": ""
             },
             "return_format": "array",
             "preview_size": "thumbnail",
             "library": "all",
             "min_width": "",
             "min_height": "",
             "min_size": "",
             "max_width": "",
             "max_height": "",
             "max_size": "",
             "mime_types": ""
         }
    ]
  }
}

A tip for becoming familiar with the format and options for each field type is to first create the field using the default ACF interface and then export it as a file using the options under the Custom Fields > Tools menu.

Image is a required field.

And finally, this is how our component would be rendered on the admin pages for content editors:

Image is a required field.

ACF Field Group Composer

The ACF Field Group Composer plugin allows us to reuse any field group that we’ve previously defined. With this added feature, we now have two powerful new ways of working with our components. The first is that we can combine multiple field groups in different arrangements for different content types without the need to duplicate those groups. The second capability we’ve gained is to build flexible fields out of the field groups we’ve already defined. In ACF, a flexible field is made up of “Layouts,” which are essentially groups of fields. Typically, we would need to either copy or re-write those field groups to the flexible field layout even if we already had that group configured.

Flynt Framework

Tying all of these techniques together into a working system is the Flynt Framework. Its core plugin and starter theme functions provide the tools for developing a fully component-based WordPress site.

Using the Flynt Framework’s approach, we can define the flexible field group described above in the following code:

{
 "interiorContentFields": [
   {
     "name": "interiorPageComponents",
     "label": "Page Components",
     "type": "flexible_content",
     "button_label": "Add Component",
     "layouts": [
       "Flynt/Components/BlockImage/Fields/Layout",
       "Flynt/Components/BlockWysiwyg/Fields/Layout",
       "Flynt/Components/BlockVideo/Fields/Layout",
       "Flynt/Components/BlockTable/Fields/Layout"
     ]
   }
 ]
}

The resulting admin fields will be displayed with each of the components as an option. Content editors are able to flexibly place components onto the page in a drag-and-drop style interface.

Image is a required field.

The real advantages become apparent while building larger systems than the truncated example above. These same components can be reused in any context simply by referencing them. In a real-world scenario, that may look like adding a single component to a specific content type but also as a flexible group layout option or reusing a predefined component as the contents of a repeater field with as little effort as a single additional line of code.

By developing a component-based WordPress theme in this way, you will be empowering content editors with the flexibility to build out pages using any number of components that have been exposed to the page. In addition to creating an editing experience that clients will love, you will also have a codebase that is more flexible, organized, and easier for your team to build upon and maintain.