If you are still following this series, you learned a lot of basics in the previous parts and are pretty well informed about the creation of Gatsby sites with WordPress.
This part will introduce a lot of new functionality, ideas and concepts. Therefore, I decided to make a video, where I will talk you through all the steps mentioned in this written tutorial. I hope this will help you to understand what is happening and why.
Note: We don't have any proper styling in place yet. So I will keep things as simple as possible. I want to refactor everything later on with theme-ui and emotion.
Now let's move forward and build a page builder with Advanced Custom Field's Flexible Content field.
Table of Contents
- Install WordPress Plugins 💾
- Create ACF fields and Content ✏️
- Add layouts to Gatsby 📰
- Improve performance through Template-String technique 🎩
- Explanation Video 🎥
- Final Thoughts 🏁
- What's Next ➡️
Install WordPress Plugins 💾
1.) Install Advanced Custom Fields PRO and activate your license. We need the PRO version if we want to use the flexible content field.
2.) Install and activate Classic Editor editor from the plugin admin interface. It will help you to only show the page builder fields and hide the content editor where we don't need it.
3.) You can download the .zip files of the wp-graphql-acf
repository and install it through WP-Admin or just navigate to your plugin folder and do a git clone
like so:
git clone https://github.com/wp-graphql/wp-graphql-acf
The WPGraphQL for Advanced Custom Fields is the plugin, that exposes the ACF fields to the GraphQL Schema. There is some good documentation of it here.
Create ACF fields and Content ✏️
We will add a flexible content field and 2 layouts to start with.
Note: If you want to skip this parts 1.) and 2.), I prepared a
.json
export for the 2 layouts. Find it right here.
1.) Create Flexible Content field
- As you can see we called the field group
Page Builder
- We added one field of type
Flexible Content
with the namelayouts
- In the
Location settings
we add the rulePost type is equal to Page
, so the page builder will be only visible for our pages, but not our posts.
- At Hide on screen make sure you selected
Content Editor
. This will hide the default editor on the pages where we use the page builder. - When you scroll down you see
GraphQL Field Name
in the Settings tab. Make sure to give it a camelCase name. This will be the field name for the GraphQL schema.- We call it
pageBuilder
- We call it
2.) Create Layouts inside the Flexible Content field
- Always make sure to have
Show in GraphQL
to be turned on. - Inside the flexible content field we add layouts with some fields like in the picture above. We add two for now:
Hero
andTextBlock
- The name of the layout usually is something with underscore, like
text_block
. This will then transform intotextBlock
in your GraphQL schema.
3.) Update your Pages
Now that we have the ACF fields setup, we need to update our pages.
- Delete the content in the normal Gutenberg blocks, if there has been content before.
- At the bottom open the Page Builder tab and
Add Row
- Add your blocks and fill in some data.
- I'm using the image for the
Hero
block and still leave it in thewfeaturedImage
. This will give me the option to use a different featuredImage for social previews if I want to do so, later on.
4.) Checkout the Schema
Run your Gatsby with yarn clean && yarn develop
and got to http://localhost:8000/___graphql to see what happend int the schema.
If you are using the same ACF layouts like me, you should be able to query the following:
query GET_LAYOUTS {
wpgraphql {
pages {
nodes {
pageBuilder {
layouts {
... on WPGraphQL_Page_Pagebuilder_Layouts_Hero {
text
fieldGroupName
textColor
image {
sourceUrl
}
}
... on WPGraphQL_Page_Pagebuilder_Layouts_TextBlock {
backgroundColor
fieldGroupName
textColor
text
}
}
}
}
}
}
}
- Check the results and get familiar with your schema.
Add layouts to Gatsby 📰
Let's start by adding our layout components. The folder structure for our layouts will look like so:
1.) Add Hero layout
// src/layouts/Hero/Hero.js
import React from "react"
import FluidImage from "../../components/FluidImage"
const Hero = ({ image, text, textColor }) => {
return (
<section style={{ position: "relative" }}>
<FluidImage image={image} style={{marginBottom: '15px'}}/>
<p style={{
color: textColor,
position: "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
fontSize: '40px',
textAlign: 'center',
paddingTop:'80px',
lineHeight: 1,
}}>{text}</p>
</section>
)
}
export default Hero
- A simple React component with some styling.
// src/layouts/Hero/Hero.data.js
module.exports = () => {
return `
... on WPGraphQL_Page_Pagebuilder_Layouts_Hero {
fieldGroupName
image {
sourceUrl
altText
imageFile {
childImageSharp {
fluid(maxHeight: 400, quality: 90, cropFocus: CENTER) {
...GatsbyImageSharpFluid_tracedSVG
}
}
}
}
text
textColor
}
`
}
-
WPGraphQL_Page_Pagebuilder_Layouts_Hero
is the UnionType name of this layout. As we can have multiple layouts, GraphQL union types (more here), help us to say: "If the data is of a certain type, then query with the following fields". - As you can see this time we use a function to export the data. This will come in handy later, if we need to adjust the query, based on certain variables.
// src/layouts/Hero/index.js
export { default } from './Hero';
- This simply helps us to make our component call a bit more elegant.
- Instead of importing like
import Hero from "../layouts/Hero/Hero"
, we can omit the last/Hero
and doimport Hero from "../layouts/Hero
instead.
2.) Add TextBlock layout
// src/layouts/TextBlock/TextBlock.js
import React from "react"
const TextBlock = ({ text, textColor, backgroundColor }) => {
return (
<section style={{backgroundColor: backgroundColor}}>
<div style={{
color: textColor,
padding: '30px'
}}>
<div style={{
color: textColor,
textAlign: 'left',
}} dangerouslySetInnerHTML={{__html: text}} />
</div>
</section>
)
}
export default TextBlock
// src/layouts/TextBlock/TextBlock.data.js
module.exports = () => {
return `
... on WPGraphQL_Page_Pagebuilder_Layouts_TextBlock {
fieldGroupName
text
textColor
backgroundColor
}
`
}
// src/layouts/TextBlock/index.js
export { default } from './TextBlock';
- TextBlock should be self-explanatory.
3.) Add data to our page queries
Now that we defined the data for the different layouts, we need to add it to our main query.
Update pages data.js
// src/templates/page/data.js
const PageTemplateFragment = (layouts) => `
fragment PageTemplateFragment on WPGraphQL_Page {
id
title
pageId
content
uri
slug
isFrontPage
featuredImage {
sourceUrl
altText
imageFile {
childImageSharp {
fluid(maxHeight: 400, maxWidth: 800, quality: 90, cropFocus: CENTER) {
...GatsbyImageSharpFluid_tracedSVG
}
}
}
}
pageBuilder {
layouts {
${layouts}
}
}
}
`
module.exports.PageTemplateFragment = PageTemplateFragment
- We update our data to be a function and pass it the layouts strings.
- This helps us to dynamically render all the queries into our main query.
Update createPages.js
// create/createPages.js
const { getAllLayouts } = require("./utils")
... 1.)
const GET_PAGES = (layouts) => `
${FluidImageFragment}
${PageTemplateFragment(layouts)}
query GET_PAGES($first:Int $after:String) {
wpgraphql {
pages(
first: $first
after: $after
# This will make sure to only get the parent nodes and no children
where: {
parent: null
}
) {
pageInfo {
hasNextPage
endCursor
}
nodes {
...PageTemplateFragment
}
}
}
}
`
... 2.)
module.exports = async ({ actions, graphql, reporter }, options) => {
/**
* Get all layouts data as a concatenated string
*/
const layoutsData = getAllLayoutsData()
... 3.)
const fetchPages = async (variables) =>
/**
* Fetch pages using the GET_PAGES query and the variables passed in.
*/
await graphql(GET_PAGES(layoutsData), variables).then(({ data }) => {
...
This snippet is not complete. Refer to this file (createPages.js), if you want to see the whole file.
I divided the file in 3 parts, where changes take place.
- We rewrite
GET_PAGES
to be a function and pass itlayouts
. Then we pass layouts further down to our template fragment like:PageTemplateFragment(layouts)
. - In the second part we call our utility function
getAllLayoutsData()
. We will add this utility function in the next step. - In part 3 we change the
GET_PAGES
call toGET_PAGES(layoutsData)
to pass down the layout data string.
Create utility function getAllLayoutsData
While we could import all the layouts data by hand and then combine the string before we pass it to our query, I wanted a way to automate/abstract the creation of the query string. Therefore this function uses the glob pattern to get all layouts/**/*.data.js
and combines them to have a query ready string.
First, add the glob module:
yarn add glob
Then add utils.js to our create folder:
// create/utils.js
const path = require("path")
module.exports.getAllLayoutsData = () => {
const glob = require("glob")
let allLayoutsString = ""
const fileArray = glob.sync("./src/layouts/**/*.data.js")
fileArray.forEach(function(file) {
let queryStringFunction = require(path.resolve(file))
allLayoutsString = allLayoutsString + " \n " + queryStringFunction()
})
return allLayoutsString
}
- So we create a file array with the
glob.sync
call at first. - Then, for each file, we require the file which essentially is an exported function (our
*.data.js
). - At last, we concatenate all the strings and return
allLayoutsString
. This will hold all our layouts data query strings.
I find, this comes in so handy!!!
4.) Add AllLayouts component
Now we need a way to decide, which component should be rendered. To make this simple first, we introduce an AllLayouts component, that depending on the fieldGroupName
(type) of the layout, decides what to render.
// src/components/AllLayouts.js
import React from "react"
import Hero from "../layouts/Hero"
import TextBlock from "../layouts/TextBlock"
const AllLayouts = ({ layoutData }) => {
const layoutType = layoutData.fieldGroupName
/**
* Default component
*/
const Default = () => <div>In AllLayouts the mapping of this component is missing: {layoutType}</div>
/**
* Mapping the fieldGroupName(s) to our components
*/
const layouts = {
page_Pagebuilder_Layouts_Hero: Hero,
page_Pagebuilder_Layouts_TextBlock: TextBlock,
page_default: Default
}
/**
* If layout type is not existing in our mapping, it shows our Default instead.
*/
const ComponentTag = layouts[layoutType] ? layouts[layoutType] : layouts['page_default']
return (
<ComponentTag {...layoutData} />
)
}
export default AllLayouts
- This component simply takes in the
layoutData
and depending on thefieldGroupName
renders the component, that we have assigned in the mapping. - Note: The fieldGroupName starts with a lowercase letter.
5.) Update Page template
Now that our data pipeline has been handled and we have a component, that dynamically renders the right component, we only need to add it to our template.
// src/templates/page/index.js
import React from "react"
import Layout from "../../components/Layout"
import SEO from "../../components/SEO"
import AllLayouts from "../AllLayouts"
const Page = ({ pageContext }) => {
const {
page: { title, pageBuilder },
} = pageContext
const layouts = pageBuilder.layouts || []
return (
<Layout>
<SEO title={title}/>
<h1> {title} </h1>
{
layouts.map((layout, index) => {
return <AllLayouts key={index} layoutData={layout} />
})
}
</Layout>
)
}
export default Page
- We simply add the AllLayouts component and add the variable
pageBuilder
coming from ourpageContext
. -
const layouts = pageBuilder.layouts || []
helps to have a fallback array, if there are no layouts defined. Then our mapping we just render to nothing and we don't end up with errors. - Then we only need to map through our
layouts
and render theAllLayouts
component, passing down thelayoutData
.
Eh voilà, we have a page builder. We can define layouts/components in our WordPress backend with ACF and dynamically render React components, depending on the type.
Improve performance through Template-String technique 🎩
I won't explain too much detail in this written part. Please watch the video for it.
One problem with our AllLayouts
component we have, is, that the way we implemented it now, on every page, we import all layout components we have, even if we don't need them.
This is fine if we have just a few components, but imagine with have 50 different layouts in our page builder. We don't want them all imported on every page.
For that we will use a little hacky, but in my opinion efficient way to generate templates out of template strings.
1.) Add more utility functions
// create/utils.js
/**
* Creates files based on a template string.
*
* @param {string} templateCacheFolderPath - Path where the temporary files should be saved.
* @param {string} templatePath - Path to the file holding the template string.
* @param {string} templateName - Name of the temporary created file.
* @param {object[]} imports - An array of objects, that define the layoutType, componentName and filePath.
* @returns {Promise<>}
*/
module.exports.createTemplate = ({ templateCacheFolderPath, templatePath, templateName, imports }) => {
return new Promise((resolve) => {
const fs = require("fs")
const template = require(templatePath)
const contents = template(imports)
fs.mkdir(templateCacheFolderPath, { recursive: true }, (err) => {
if (err) throw "Error creating template-cache folder: " + err
const filePath = templateCacheFolderPath + "/" + ((templateName === "/" || templateName === "") ? "home" : templateName) + ".js"
fs.writeFile(filePath, contents, "utf8", err => {
if (err) throw "Error writing " + templateName + " template: " + err
console.log("Successfully created " + templateName + " template.")
resolve()
})
})
})
}
/**
* Creates pages out of the temporary created templates.
*/
module.exports.createPageWithTemplate = ({ createTemplate, templateCacheFolder, pageTemplate, page, pagePath, mappedLayouts, createPage, reporter }) => {
/**
* First we create a new template file for each page.
*/
createTemplate(
{
templateCacheFolderPath: templateCacheFolder,
templatePath: pageTemplate,
templateName: "tmp-" + page.slug,
imports: mappedLayouts,
}).then(() => {
/**
* Then, we create a gatsby page with the just created template file.
*/
createPage({
path: pagePath,
component: path.resolve(templateCacheFolder + "/" + "tmp-" + page.slug + ".js"),
context: {
page: page,
},
})
reporter.info(`page created: ${pagePath}`)
})
}
- Most of the things should be explained with the comments I added. Basically we create new template files based on a template-string file.
- Then we create the pages, based on that generated file.
2.) Add layoutMapping.js
module.exports = {
page_Pagebuilder_Layouts_Hero: "Hero",
page_Pagebuilder_Layouts_TextBlock: "TextBlock",
}
3.) Add lodash.uniqby
and lodash.isempty
yarn add lodash.uniqby lodash.isempty
4.) Add/update createPages.js
First the top part:
// create/createPages.js
const _uniqBy = require("lodash.uniqby")
const _isEmpty = require("lodash.isempty")
const { getAllLayoutsData, createTemplate, createPageWithTemplate } = require("./utils")
const filePathToComponents = "../src/layouts/"
const templateCacheFolder = ".template-cache"
const layoutMapping = require("./layouts")
const pageTemplate = require.resolve("../src/templates/page/template.js")
Then further down:
// create/createPages.js
wpPages && wpPages.map((page) => {
let pagePath = `${page.uri}`
/**
* If the page is the front page, the page path should not be the uri,
* but the root path '/'.
*/
if (page.isFrontPage) {
pagePath = "/"
}
/**
* Filter out empty objects. This can happen, if for some reason you
* don't query for a specific layout (UnionType), that is potentially
* there.
*/
const layouts = page.pageBuilder.layouts.filter(el => {
return !_isEmpty(el)
})
let mappedLayouts = []
if (layouts && layouts.length > 0) {
/**
* Removes all duplicates, as we only need to import each layout once
*/
const UniqueLayouts = _uniqBy(layouts, "fieldGroupName")
/**
* Maps data and prepares object for our template generation.
*/
mappedLayouts = UniqueLayouts.map((layout) => {
return {
layoutType: layout.fieldGroupName,
componentName: layoutMapping[layout.fieldGroupName],
filePath: filePathToComponents + layoutMapping[layout.fieldGroupName],
}
})
}
createPageWithTemplate({
createTemplate: createTemplate,
templateCacheFolder: templateCacheFolder,
pageTemplate: pageTemplate,
page: page,
pagePath: pagePath,
mappedLayouts: mappedLayouts,
createPage: createPage,
reporter: reporter,
})
})
5.) Add string based template
In JavaScript you can use template strings to pass down arguments or logic in general to your string. We make use of that to literally create a template based on our src/templates/page/index.js
component.
module.exports = (imports) => {
return`
// This is a temporary generated file. Changes to this file will be overwritten eventually!
import React from "react"
import Layout from "../src/components/Layout"
import SEO from "../src/components/SEO"
// Sections
${imports.map(({ componentName, filePath }) => `import ${componentName} from '${filePath}';`).join('\n')}
const Page = ({ pageContext }) => {
const {
page: { title, pageBuilder },
} = pageContext
const layouts = pageBuilder.layouts || []
return (
<Layout>
<SEO title={title}/>
<h1> {title} </h1>
{
layouts.map((layout, index) => {
${imports.map(({ componentName, layoutType }) => {
return `
if (layout.fieldGroupName === '${layoutType}') {
return <${componentName} {...layout} key={index} />;
}
`
}).join('\n')}
})
}
</Layout>
)
}
export default Page
`
}
- What we do here, is to decide what components actually get imported. We only want to import components, that are part of the created page. Not more not less.
Explanation Video 🎥
This tutorial can be a lot to take in and it is hard for me to write a super detailed explanation of everything down. So I decided to record a video, where I would go through the steps mentioned in this tutorial and verbally explain what my thoughts to the implementation choices are.
I hope I find the time to finish the video soon. Stay tuned.
-> Video - Coming soon <-
Final Thoughts 🏁
Boom, now we have a way to dynamically render blocks created through ACF flexible content layouts. Surely this is not a full-fledged page builder yet, as we we can't use layouts inside layouts. But, we can add and order section by section, which for most use-cases, should be more than enough. This approach would now allow you to create reusable layouts and therefore is a powerful way to create your pages.
There is also a way to use the Gutenberg blocks with the wp-graphql-gutenberg plugin created by Peter Pristas. While this is a valid way to work with WordPress, I'm not the biggest fan of how to implement Gutenberg blocks and work with the editor. This might change though, with how things develop.
Checkout how the site looks now here: https://gatsby-starter-wordpress-advanced.netlify.com/
Find the code base here: https://github.com/henrikwirth/gatsby-starter-wordpress-advanced/tree/tutorial/part-7
What's Next ➡️
Coming up soon: Part 8 - Internationalization
Top comments (37)
Thank you Henrik. this has been very helpful! I learnt a lot.
I kept running into an error though. So eventually I cloned your part-7-posts branch (seems like the latest), but I still run into the same error. I'm not sure how to solve it.
The first error I get is this, but I've seen it on other sites too, it's something to do with NPM:
But then it carries on compiling until I get this, the same error I got on my own version:
It seems to happen on different pages each time. I am using an existing WP install. I did not put the pageBuilder blocks on all the pages, just one page to test on. So don't know if it could be that?
The posts branch is a little special and is just done because someone asked for that in the comments. It is not explained in the tutorial though.
The deprecation warning comes from the node.js version you are using. It shouldn't effect your build. I get the same.
Not sure why it has problems afterwards. Maybe you could try to downgrade to another node version with github.com/tj/n
Hi Henrik. I got nvm up and running. So I'm using node 10.18.1 and npm 6.13.4. I cleared my Gatsby cache, reinstalled node_modules, etc etc. But I still run into that issue.
Look, it's not a huge pain. It was working fine up until the 6th part of your tutorial. As you said it's probably a Windows issue then.
Just thought I'll give you feedback.
Thanks Henrik. I notice the version manager is not supported on Windows. There is an upgrade available. I tried it now, but still no luck.
When I get a chance later I'll manually uninstall and reinstall Node 10. I'm on 12.14.1 LTS now. I see that deprecation warning seems to be common and people just downgrade to make it go away.
Ah, if you are on windows, it could be an issue with my code and the paths I'm using when creating the templates. Haven't tested it on Windows yet, sorry. But yeah, maybe try the Node version first. Maybe this can help? github.com/coreybutler/nvm-windows
Ah, the Windows path actually makes sense if you look at that error.
Thanks for that link. I see there is bit of a process involved, so I'll have to make time for it a bit later. Have some work to catch up on.
Thanks for your time and effort Henrik. Much appreciated.
I'm experiencing the same issue (I'm on a Mac):
/Users/me/sites/gatsby-starter-wordpress-advanced/node_modules/yoga-layout-prebuilt/yoga-layout/build/Release/nbind.js:53
throw ex;
^
Error writing tmp-/new-page/ template: Error: ENOENT: no such file or directory, open '.template-cache/tmp-/new-page/.js'
Were you ever able to get that sorted? Thanks!
Sorry, unfortunately not.
Sorry, that I can't help more, but my time will be super limited for the next 1-2 months :/
But from what I can see in your error messages:
It seems to be a path problem. First of all both have a slash or backslash (Windows) in front of the
.js
So for Björn it seems to be the only problem.For you Ezra, it seems there is more going on. The path should rather look like this:
Maybe this helps to debug my code:
create/utils.js
createPageWithTemplate
console.log(page.uri)
My guess is, that your page.uri has a slash before and/or after. This will lead to problems in this setup. You'd need to make sure it does not include slashes. Actually when I look at it, it might make more sense to use
page.slug
instead. For that you would need to add it tosrc/templates/page/data.js
.You would also need to be sure though, that there is no duplication of a slug. Otherwise it will overwrite the template from the duplication.
Thanks Henrik. Have been a bit overloaded. Will get back to this in due time and will check out your suggestions.
Did anyone find a solution to this? I am having the same issue. I'm on a Windows PC:
C:\Users\Tobias\AppData\Roaming\npm\node_modules\gatsby-cli\node_modules\yoga-layout-prebuilt\yoga-layout\build\Release\nbind.js:53
throw ex;
^
Error writing tmp-/ template: Error: ENOENT: no such file or directory, open 'C:\Users\Tobias\Desktop\Gatsby\gatsby-starter-wordpress-advanced.template-cache\tmp-.js'
Hi guys, I updated the code and tutorial. There were some path issues with the new WPGraphQL version. I tested it now on my Windows PC and it works for me. Checkout the master or part-7 branch and see if it solves your issues now.
Hi Henrik, this is a great series, I really appreciate your work in sharing this with us.
How would I extend this if I wanted to use ACF page builder fields on posts in addition to pages?
Eagerly awaiting the next one ;)
Hi James,
glad you enjoy the series. First of all you would need to add the pageBuilder to your Posts with ACF like so:
Then it is a similar process like with the pages. I created a branch to make it more easy for you. Checkout the last commit to see the changes. There are quite a few, but it is basically just to ensure certain functions or certain data work with either posts or pages.
I only tested it really quickly, so there is a high chance, that something still needs some tweaking.
henrikwirth/gatsby-starter-wordpre...
Hi Henrik,
Wow, that's awesome! Thank you so much. I'll definitely checkout that branch.
James
Let me know if it works and feel free to share your work. Always interested in what people build with this stack :)
Tested it out with a custom ACF layout on a post.
Works marvellously ;)
Hi Henrik, thanks again for the series. Are you planning on extending it to cover expanding the page builder into a more complete one? Also, do you have any ideas on how to implement previews on the wp side?
Hi Joel,
What exactly would you consider a more complete page builder? Do you mean just more ACF layouts, that together make it possible to design a more or less complete website?
If that is of interest, I surely could build some standard sections within this starter project. Its pretty simple to add layouts yourself though, which I wanted to teach with this tutorial. So everyone can built it to their needs.
I do. I already managed to implement previews in a personal project. It's possible to use the posts's draft data for it. So the idea is to have some sort of
preview.js
page, that uses Apollo GraphQL queries to dynamically fetch from your WordPress GraphQL endpoint. The way I wrote the queries with template strings, give you the possibility to reuse those queries for your dynamic content (with some little refactoring).I want to talk about previews in one of the upcoming tutorial parts. I can't tell you when though, as my time is limited at the moment.
Yeah, I had to familiarise myself with ACF - I initially thought that it would work like drag-and-drop page builders such as elementor :)
Thanks for the preview tips and looking forward to any upcoming parts.
Fantastic series, Henrik! I have learnt a lot - I am currently attempting to migrate one of my WP sites over to Gatsby and Headless WP. It is great fun working with Gatsby, but it is easy to hit roadblocks along the way.
Really glad you enjoy the series pjs. I feel you. In the beginning I had a lot of troubles adapting to this way of developing a front-end. There is so much to figure out. Thats why I thought the tutorial might save some peeps from sleepless nights 🤷♂️
Hello Henrik,
Thank you for writing up these series of tutorials, very comprehensive!
I have a question regarding your starter, would it be possible to migrate from the gatsby-source-graphql plugin to gatsby-source-wordpress-experimental? I have attempted to do this myself but as a beginner I cannot make heads or tails of what is needed to rewrite the queries, resulting in some frustrating errors while trying to run my dev server.
Could you point me in the right direction?
Hi Ruben. Sadly, this stack is already pretty old now. I have an example gatsby theme, that uses the new experimental version, but I don't have much time right now for a new tutorial on it. Here is the link, maybe it explains itself a bit.
github.com/henrikwirth/gatsby-star...
Thank you Henrik, I'll have a look at it!
Hi. Firstly, this is a great set of tutorials, thanks. I am left a tad confused with this last however one regarding how the layouts display in wp-admin and have a (sorry rather long winded) question -
Ahh, ive just set this up myself and now I see. It really is a full blown page builder, damn that's clever, when I usually make sites using ACF and themes I tend to just use the content types required, this gives a lot more flexibility. good work!
It is such a long tutorial and introduces so many custom approaches that I can totally understand if one gets lost. Thats why I actually wanted to make an explanation video for it, but I really don't have the time at the moment.
Glad you worked it out though. It is still not a full blown page builder, more like a section builder actually. But I feel it serves most of my purposes and it is dynamic enough :)
Thanks Henrik, great tutorial - easy to follow and helped me build my first Gatsby+Wordpress site, albeit just one page!
To help me go one step further, do you have any examples where the field type is Post Object and it's set to Select multiple values?
If it's just one value, the graphQL below works and I can render the value in the template. However, if I select multiple objects, graphQL returns NULL. Do you know if this is incorrect graphQL or do I have to grab all the array values within Gatsby? Thanks in advance!!
... on WPGraphQL_Page_Pagebuilder_Layouts_PickFeaturedCards {
fieldGroupName
featuredCards {
... on WPGraphQL_Page {
id
title
}
}
}
Looks like it's an unsolved issue: github.com/wp-graphql/wp-graphql-a....
Solution, for now at least, is to use a repeater rather than multiple objects.
Actually forgot to update the site on Netlify.
Now it is up and running here: gatsby-starter-wordpress-advanced....
Thank you Henrik, great tutorial.
Could you add blog category pages to the repository? Unfortunately, I can't handle it for two days. I'd appreciate it.
Hi Chesterros,
I won't have time for that for the next 2 months, but there are a couple of projects out there, that do that already. So you could take them as example and work it inside my approach.
github.com/zgordon/twentynineteen-...
github.com/staticfuse/staticfuse/t...
These are two examples that have categories.
And I just made another project, that uses the new gatsby-source-wordpress@v4 plugin.
github.com/henrikwirth/gatsby-star...
Thank you! This working, great :)
Great inspiring post.
If i had the time / was more knowledgeable i'd do this but using grapesjs.com/ instead of wordpress.
Well, if thats a tool, that serves your needs, then thats great. There is so much technology out there, so it is hard to keep up with everything ... and really, there is no need for it most of the time.
That being said, trying out Gatsby can be a lot of fun if one has the time to do so :)