One of the largest draws of headless or decoupled WordPress architecture is that we can endlessly remix the WordPress CMS that content creators love with the latest and greatest of web technology. This presentation will demonstrate how quickly a developer can get up and running with headless WordPress development using the Remix full stack framework. Using open source tools like WPGraphQL and Atlas Content Modeler, you can transform your WordPress site into a powerful headless CMS that serves data via GraphQL. We’ll see how we can query for and use that data in a Remix app, and also discuss how the framework’s focus on web fundamentals can benefit both the developer and the visitors who use our sites.
Remixing WordPress: Building a Headless Site with Remix, WPGraphQL, and Web Fundamentals
AI Generated Video Summary
In this Talk, the concept of headless WordPress and its benefits for developers are discussed. The use of front-end frameworks like Remix, Nuxt, Next, or SvelteKit to interact with WordPress through REST or GraphQL APIs is highlighted. The process of creating content models, adding data, and querying the GraphQL schema is explained. The setup of a basic Remix app with Apollo Client and the loading of data into route components using Remix are covered. The handling of dynamic routing with Remix and WordPress is also explored.
1. Introduction to Headless WordPress
In this part, we discuss the concept of headless WordPress and its benefits for developers. We compare traditional WordPress with headless WordPress, highlighting the flexibility it offers. We also mention the use of front-end frameworks like Remix, Nuxt, Next, or SvelteKit to interact with WordPress through REST or GraphQL APIs. The ultimate benefit is that the experience for content creators remains the same, while developers can leverage the advantages of modern front-end frameworks. We then move on to building a headless site using local development tools and installing necessary plugins like WP GraphQL and Atlas Content Modeler.
What's up, React Summit? My name's Jeff Eberhardt, and I'm a Senior Developer Advocate at WP Engine. The title of my talk today is Remixing WordPress, where we're going to spend about 20 minutes building a headless site with Remix, WP GraphQL, and a few other open-source plugins.
But before we get started, maybe we could talk broadly about what headless WordPress is. With the rise in JAMstack architectures, we see a ton of SaaS offerings in the headless CMS space. But if you're looking for an open-source alternative, a lot of people don't know that WordPress can also become a headless CMS. And with WordPress being the most popular content management system, which is powering roughly 40% of the web at the time of this recording, there are chances that your content teams are already well-versed in the WordPress platform. So there can be a lot of reasons to consider this as an option.
So let's take a second and talk about what traditional WordPress gives us. Traditional WordPress is your basic monolithic software platform. The visitor maybe interacts with a CDN by typing in a URL into their browser bar. Ultimately, all of those requests are served by WordPress Core. Any of the developer experience that needs to happen, whether that's theming or plugin development, happens directly with that WordPress Core software, as do the publisher interactions. When people are creating content, they're doing that as a part of the WordPress monolith.
Now when we look at headless WordPress in comparison, that offers the developer a lot of flexibility. The experience for the visitor is still the same. They type this URL into a browser bar, but instead of their request being handled by WordPress Core, it is actually handled by an application that we ultimately determine what that looks like. In the example today, we're going to use Remix as this Node.js runtime, but that could be any potential front-end framework, whether you're a fan of Nuxt or Next or you want to use SvelteKit. That is entirely up to you as a developer, and all of your effort is going to be into developing that platform. And all of that interacts with WordPress through either the REST or GraphQL APIs that we're going to explore today, that are a part of WordPress Core or added via plugins. But the ultimate benefit is that the experience for the publisher or the content creator doesn't change, and they get to use WordPress sort of as-is to create published content, while we as the developers get to take the benefits happening in the front-end framework space and bring those to our users.
So let's get started. We don't have a ton of time, so I'm going to hop right into building out our headless site. So the first thing that we'll need to do is create a new WordPress site. And I'm going to do that using the local development tools. I'll click create new site, give my site a name, select the preferred customization option, create an admin username and password, and then I'll let local install those services for me on my local machine. If you have a WordPress site already, feel free to use that as well and install the plugins there. Once that's done installing, we can open up the WP admin panel, log in, and then we'll need to install a few plugins. First we'll want to install the WP GraphQL plugin, which essentially turns our WordPress into a WP GraphQL API. We can install and activate that plugin. And then we will also be installing a plugin called Atlas Content Modeler, which is a headless focused content modeling plugin that will allow us to create complex content types and make those available over GraphQL immediately.
2. Creating Content Model and Adding Data
Now with our plugins installed, we can create our first content model using Atlas Content Modeler. We start by specifying the name, visibility, icon, and description of the model. Then, we add fields such as project name, due date, assignee's name, and project description. After creating the model, we can add data by filling out the fields and publishing the content. We can explore the features of WPGraphQL and the Graphical IDE to query our GraphQL schema and retrieve data from our WordPress site. Finally, we discuss integrating Remix into our WordPress and GraphQL setup.
Now with our plugins installed, we can work on creating our first content model. To do that, we'll use Atlas Content Modeler to create a content model around projects. Do that by specifying a singular and plural name, determining the API visibility of our content model, whether it's public or private, choosing an icon for the post-editing screen, and creating a description for our users to see. When we click the create button, that will automatically generate the model and begin the process of adding it to our GraphQL API.
With our model created, we can begin the process of adding fields to our model. We'll start with a text field that stores the name of the project. We'll use that as the entry title, make it required, and add it to our model. After that, we can add a date model that will store the due date for the project, make that required as well, and add a description for that model that will be displayed to our users. From there, we will add an additional text field to store the assignee's name for the project, and this will be the DevRal associate who is assigned to a particular piece of content. Make that required as well, and then we will add one last rich text field that will be the project description.
So now that we've created our project content model, let's add some data. We'll open up the post-editing screen and fill out all of the fields that we specified as we created the model, and then when we're done, we can publish that piece of content to make it available for use in our application. Now that we've seeded our project with some additional data, let's explore the features of WPGraphQL and the Graphical IDE. The query composer feature of the Graphical IDE allows us to visually explore our GraphQL schema and select individual content types and their nodes and properties, all while composing a GraphQL query that we can run dynamically and to return actual data from our WordPress site. So now that we understand the resources available to us in WordPress to model data and to serve that data over GraphQL, let's talk about how we're going to integrate Remix into that equation.
3. Setting Up Remix App and Configuring Apollo Client
In this part, we set up a basic Remix app with dependencies like GraphQL, Apollo Client, and Tailwind CSS. We import components in our index.js route and render test data. We configure Apollo Client by creating an Apollo.js file, setting up the client, and creating an in-memory cache. We create a WordPress service file to contain our data-fetching code, import the client and GQL object, and query the WordPress WP GraphQL instance. We return the individual project nodes from the response. Organizing the data fetching code is flexible, and Remix allows different approaches.
So I've got a pretty basic Remix app set up right here with a few dependencies installed that we're going to need to communicate with our WordPress server. So one of those is the GraphQL package. I've also installed Apollo Client. And I've done some styling with Tailwind CSS.
So where we can get started is in our index.js route. We can look here, we can see that we're importing two different components. We've got just a basic header and a basic project component that's going to list our projects out in cards on this page.
So what I'll do over here is in my Terminal, just click NPM run dev to get this dev server running. Then I can come back over here and we'll just refresh on localhost 3000 and see that this test data array, where I've got some projects that we'll eventually swap out with data from our WordPress site, is rendering right there for us.
So the next thing that we'll want to do is configure Apollo Client to query our GraphQL endpoint from our WordPress site. To do that, we'll begin by creating an Apollo.js file in the Lib folder. Then we'll add some import statements to import those needed resources from that package. Then what we'll do is create a default export that is going to be our client and we'll set that equal to new Apollo Client. Then we'll pass in some configuration parameters. One of the ways we can get our GraphQL endpoint is to hop back into our WordPress site, open up the WP GraphQL Settings page, and copy and paste that into our project. Then we'll also create a new in-memory cache that we can use to help speed up our application. Once we're done, we'll save those things out.
So now that we've configured Apollo Client, I'm going to create a new file to contain my data-fetching code. Really this part is entirely up to you about how you want to structure or where you want this code to live. So I'm going to create a file called WordPress service. In there I'm going to import the client that we configured in the last step and then also the GQL object from Apollo Client. So then we'll create an export and export a function called get projects. Then from here, what we'll do is hop back into our WordPress WP GraphQL instance, copy the query that we were using, and then we'll create a new query, open up a new template literal with GQL and paste that query in there. Then what we'll do is create a new variable called response and we will essentially use the client to query and pass in that query to that configuration object. And then once we get that response back from Apollo Client, we will dig down into the object that we get back so that we return the individual project nodes. It's always helpful if you're unsure of how to traverse a particular object, you can always hop back into WP GraphQL and visually look at what the data looks like that you're going to get back from that API call.
OK, so now that we've got this function stepped out to make our GraphQL query, let's pause for a second and just take stock of where we're at. It's worth mentioning that although I've created this WordPress service file with these export functions, how you handle organizing this data fetching code is really entirely up to you. I know lots of React developers in particular are fans of co-location, so having the code that does their data fetching also inside of their components, there are ways that you can achieve that as well by having some of this code happen in the individual route files. So this is just a choice that I've made because I like this organizational pattern, but that doesn't mean that you couldn't make another choice, and Remix is unopinionated about this as well.
4. Loading Data with Remix
In this part, we explore how to load data into route components using Remix. We discuss the opinionated nature of Remix and its file-based routing system. We import components and create an async function named loader to fetch data from WordPress using the use loader data hook. We explain the benefits of separating data calls into a service file and highlight the simplicity and security advantages of server-side data fetching. Finally, we have our index page working and each link represents an individual project.
As we transition into looking into how we use this function to load data into our route components, Remix is actually opinionated, and we'll talk about some of those differences. Next, we'll take a look at this index.jsx file in our routes folder. The Remix framework uses a file-based routing system, which is kind of similar to what you might get in Next.js or Nuxt if you're coming from the Vue world, so there is a decent amount of overlap in how this framework handles routing. And we'll look at some other more complex examples in just a little bit.
But so, if we open up our index.jsx file, we can see that we're importing some components in here, and then we're exporting this index component that returns all of these jsx elements that are going to render our application. But right now, we have all of our data hardcoded in this projects variable, and so the next step that we're going to want to tackle is loading our data dynamically from WordPress. So to achieve that, we'll just throw a couple more import statements up here, and I'm just going to copy those from offscreen real quick. We're going to import two different things. So we're going to import our get projects function from this WordPress service file, and then we're going to also import this use loader data hook from Remix itself. So once we've done that, then we'll come down here, and we're actually going to create another export, and so this is going to be an async function named loader, and that is a Remix convention. And what's going to happen is when Remix goes to render your components, it's going to call whatever is inside of this loader function on the server, and it's going to go out to your service, whatever that may be, in our case WordPress and GraphQL, to get some data, and then we will be able to access the data that this loader function returns using the use loader data hook. So for us, all we need to do in this example is add a return statement, and then we're going to await the result of get projects. So again, that's one of those reasons that I personally like to develop in this way. I like having a service file with all of my different data calls out there so that my route components can be very clean. Alternatively, you could certainly do some of this inside of this loader function. That's totally up to you and how you want to handle it, and to be honest with you, I'm not sure Remix really cares either way. But so we've got this loader function, we're doing some amount of work in there, and that's up to us to determine what it is that we want to do. And then what we'll do down here is instead of hard-coding this array we can come and we can just use the use loader data hook. So we'll set projects equal to the result of this use loader data function, and so if we save that out and refresh our page over here we can see that boom, that worked just as we expected. The load when this index route gets called, we go to render that component. This loader function calls get projects on the server, we return that data, and then use that data via this hook to populate these project components down here. So fantastic, we've got our index page working the way that we want, but I think it might be time to pause for just a second and discuss this opinionated choice that the Remix framework makes.
I've worked with a number of different Next.js projects and other React projects where some of the data loading that we're doing happens entirely on the client. And so if we're used to using the Apollo like use query hook, for example, and having those components make those calls out to our GraphQL server on the client, this is a bit of a paradigm shift, if you will, right, because each one of these loader functions is only getting called on the server. And I think the Remix team made that choice for a really good reason, and they've enumerated those reasons in a lot of videos, so I won't belabor them, and I encourage you to go check out some of the Remix singles to get a good idea of those. But in some ways, it does help us as developers simplify the type of code that we're writing, right? If we look at a framework like Next.js, for example, where maybe we're doing static site generation, and then some things are getting loaded on the client, it has a tendency to get a little bit messy, right, because we have all these different abstractions for these different use cases, where I think Remix sort of simplifies that. It says this is the one way that we're going to load data into components, and so it makes that a little bit easier. It also makes it a little bit easier to handle credentials, right? If I were making authenticated calls to a database, since all of these, this data fetching and the loader function is happening on the server, I don't have to worry about leaking my credentials into the client. So there are a couple of pluses to this that I definitely recommend you explore if you're new to Remix and kind of compare and contrast with whatever your framework of choice is at the time. So now that we've got our index page looking the way that we want, each one of these is actually a link to an individual project.
5. Handling Dynamic Routing with Remix and WordPress
In this part, we explore how to handle dynamic routing with the Remix framework and WordPress. We make changes to the routes folder, create a component for the dynamic route, and stub out the get project by slug function. Using the graphical IDE, we create a GraphQL query to retrieve the post by slug, format it, and integrate it into our WordPress service file. We discuss the loader function and its implementation in the slug.jsx file, which allows us to access dynamic URL components. Finally, we confirm that the index page and dynamic URL segments are functioning correctly.
Right now if we click on it, we get a 404 error. So what we'll do in the next few minutes is just dig one layer deeper and look at how we would handle this dynamic routing example with the Remix framework and WordPress.
Ok, so for our project to handle this dynamic route URL, we're going to need to make some changes to our routes folder. We can see that the URL that we're sending the application to is project slash the slug of the particular project. So inside of our routes folder, we will create a new folder called projects to match that part of the URL segment. And then inside of that projects folder, we're going to create a new file called $slug.jsx. So this is going to be the component that we export. So I'm just going to come over here real quick and copy this from off screen and paste that in just so we can take a look at it and we'll go ahead and save that out and then we'll sort of just talk through a couple of different things here and we can see already in the terminal that we've got some work that we need to do. So let's hop back, I'm going to save this one more time, we'll hop back into our WordPress service and then get going from there.
So we can see that our component is using this get project by slug function so we'll hop back in there and we'll go ahead and we'll stub that out. This isn't going to return anything at the moment, get project by slug and then this is going to take the slug as a parameter. So now that we've got this stubbed out let's hop back into our WordPress instance and this is where we use the graphical IDE to create our GraphQL query. So again using the graphical IDE in the query composer we can use those tools to help us visually create and format a new query to get our post by the slug. If we open up the individual sort of project node inside of the query composer we can use that and specify the type of ID that we want to look up the post by. So in this case we'll use slug and we'll grab an ID from our WordPress site and pass that in and then what we'll do is also add in all of the additional fields that we had on our previous query to make sure that all of the data is the same. We can just click those to add them to the query and then once we're done we can again preview the result of that query by just running it in IDE. And so once we have our graph QL formatted the way we want and making sure that it's returning the data that we want we can copy it and paste it back into our WordPress service file. We'll make a few changes inside of this GQL template literal to parameterize this query so that we can pass in the ID. Then after that we'll actually make the query with our client object and pass that formatted query into it as well as a variables property that takes an object where each property in that variables object like ID for example corresponds to one of the variables that we parameterized in the tag template literal. Then we'll also just sort of traverse our response from graph QL and return that to our loader function. And as always if you're kind of confused and you need help figuring out what your data looks like you can always use the graphical IDE to explore that.
So it's worth talking really quickly about the loader function as it's implemented in the slug.jsx file. A lot of this should look very familiar to what we did in our index file with the exception of this params object that is getting passed into the loader. This is how we access those dynamic URL components. So dollar sign slug becomes params dot slug and then we just pass it into our get project by slug function that we pull in from our WordPress service. And if we come over here and we refresh our page, you can see that everything is working as we expect, and now we have both an index page and dynamic URL segments that are looking up individual posts.
Thank you so much for coming to my talk on remix and headless WordPress. If you're interested in learning more about the open source headless CMS alternative and some of your favorite front end frameworks, be sure to follow me on Twitter or check out some of the content at developers.wpengine.com.