DEV Community

Elliott W
Elliott W

Posted on • Edited on

Gatsby Wordpress ACF Flexible Content

Let me tell you a story about some poor sod (definitely not me) who is excited about gatsby + wordpress but is lead astray by dark forces out of his control.

Let's say you want to create a page template where pages can be made out of components defined in your flexible content field. You are trying to get the data for all these components in src/templates/page.js:

export const query = graphql`
  query PageQuery($id: String!) {
    wpPage(id: {eq: $id}) {
      title
      page_components {
        components {
          __typename
          ... on WpPage_PageComponents_Components_BannerOne {
            title
            button {
              text
              link
            }
            # More stuff
          }
          ... on WpPage_PageComponents_Components_BannerTwo {
            title
            image
            # More stuff
          }
        }
      }
    }
  }
`
Enter fullscreen mode Exit fullscreen mode

It would be against the Single-responsibility principle in the SOLID principles to have the component queries as part of the page query, so you turn these component queries into fragments:

For example, in src/components/page/BannerOne/BannerOne.js:

export const query = graphql`
  fragment BannerOneFragment on WpPage_PageComponents_Components_BannerOne {
    title
    button {
      text
      link
    }
    # More stuff
  }
`
Enter fullscreen mode Exit fullscreen mode

And now your page query in src/templates/page.js looks like:

export const query = graphql`
  query PageQuery($id: String!) {
    wpPage(id: {eq: $id}) {
      title
      page_components {
        components {
          __typename
          ... on WpPage_PageComponents_Components_BannerOne {
            ...BannerOneFragment
          }
          ... on WpPage_PageComponents_Components_BannerTwo {
            ...BannerTwoFragment
          }
        }
      }
    }
  }
`
Enter fullscreen mode Exit fullscreen mode

Great! Things are looking better already. However we still haven't completely separated concerns, since when we add a new component, we have to remember to add its fragment to the page query. Not the end of the world, but not ideal either.

You notice the graphql query is a template literal, so maybe you can dynamically generate the components query? Assuming you could get the componentNames by reading all the component file names from your file system, it would look something like:

export const query = graphql`
  query PageQuery($id: String!) {
    wpPage(id: {eq: $id}) {
      title
      page_components {
        components {
          __typename
          ${componentNames.map(componentName => {
            return `
              ... on WpPage_PageComponents_Components_${componentName} {
                ...${componentName}Fragment
              }
            `
          }).join()}
        }
      }
    }
  }
`
Enter fullscreen mode Exit fullscreen mode

Ah! An error! What you are trying to do is extremely illegal! So you put up with adding new fragments to the page query every time you add a new component. But then you decide: wouldn't it be great to build other post types out of components too?

Now it starts to get messy. Not only do you have to duplicate the fragments in the page query (src/templates/page.js):

export const query = graphql`
  query PageQuery($id: String!) {
    wpPage(id: {eq: $id}) {
      title
      page_components {
        components {
          __typename
          ... on WpPage_PageComponents_Components_BannerOne {
            ...BannerOnePageFragment
          }
          ... on WpPage_PageComponents_Components_BannerTwo {
            ...BannerTwoPageFragment
          }
          ... on WpProject_PageComponents_Components_BannerOne {
            ...BannerOneProjectFragment
          }
          ... on WpProject_PageComponents_Components_BannerTwo {
            ...BannerTwoProjectFragment
          }
        }
      }
    }
  }
`
Enter fullscreen mode Exit fullscreen mode

You also have to duplicate the fragments in the components too (src/components/page/BannerOne/BannerOne.js):

export const query = graphql`
  fragment BannerOnePageFragment on WpPage_PageComponents_Components_BannerOne {
    title
    button {
      text
      link
    }
    # More stuff
  }
  fragment BannerOneProjectFragment on WpProject_PageComponents_Components_BannerOne {
    title
    button {
      text
      link
    }
    # More stuff
  }
`
Enter fullscreen mode Exit fullscreen mode

And you can imagine the pain when trying to add more post types...

Several weeks later

Then God Himself (and by God I mean Henrik Wirth) comes to you in a dream and tells you that all these problems can be fixed by generating the template file (src/templates/page.js) yourself before Gatsby gets its filthy hands on it!

This way you can dynamically generate the page query like we wanted to earlier. We can also create separate template files for each of the different post types. Fantastic! It would look something like this in gatsby-node.js:

postTypes.forEach(postType => {
  createTemplate(postType)
})
pages.forEach(page => {
    const template = getTemplate(page.postType)
  gatsbyUtilities.actions.createPage({
    path: page.uri,
    component: template,
    context: {
      id: page.id,
    },
  })
})

Enter fullscreen mode Exit fullscreen mode

The createTemplate function would create a template file for every post type, e.g. .cache/page-templates/page.js and .cache/page-templates/project.js, and the queries in each of those files would be automatically populated with all the component fragments.

But wait, we can optimize even further!

Right now, every template file imports every component that exists, which bloats pages that are not using every single component. What if we generated template files for every single page, rather than just for every post type?

This way we import only the components that a page needs.

If you'd like to see how this works in practice, please visit
gatsby-wp-acf-starter for a rigorously commented starter project.

Top comments (2)

Collapse
 
henrikwirth profile image
Henrik Wirth • Edited

:D really

I'm blushing ;)

Collapse
 
elliottw profile image
Elliott W

Haha appreciate your work, thanks mate!