{"id":31802,"date":"2025-03-19T13:18:14","date_gmt":"2025-03-19T18:18:14","guid":{"rendered":"https:\/\/wpengine.com\/builders\/?p=31802"},"modified":"2025-03-19T13:18:16","modified_gmt":"2025-03-19T18:18:16","slug":"headless-wordpress-monorepo-setup","status":"publish","type":"post","link":"https:\/\/wpengine.com\/builders\/headless-wordpress-monorepo-setup\/","title":{"rendered":"Headless WordPress Monorepo Setup"},"content":{"rendered":"\n<p>In a decoupled environment, maintaining a streamlined workflow is key to creating and managing effective apps and sites. Combining WordPress with a modern JavaScript framework like Next.js in a monorepo setup offers developers an organized way to keep frontend and backend changes in sync.&nbsp;<\/p>\n\n\n\n<p>By housing both your site\u2019s backend and frontend code within the same repository, you can streamline deployments, simplify local development, and maintain tighter integration between your systems.<\/p>\n\n\n\n<p>This guide will walk you through the steps required for setting up a monorepo that contains both backend WordPress code and frontend Next.js app code and deploying each of them to WP Engine\u2019s Headless Platform. By the end, you\u2019ll understand how to use WP Engine\u2019s tools and best practices to ensure your frontend and backend remain in sync, all while deploying both parts of your stack simultaneously. <\/p>\n\n\n\n<div class=\"wp-block-group has-polar-background-color has-background is-layout-flow wp-container-core-group-is-layout-7a03825d wp-block-group-is-layout-flow\" style=\"padding-top:var(--wp--preset--spacing--30);padding-right:var(--wp--preset--spacing--40);padding-bottom:var(--wp--preset--spacing--30);padding-left:var(--wp--preset--spacing--40)\">\n<p class=\"has-large-font-size\"><strong>Table of Contents<\/strong><\/p>\n\n\n\n<ul id=\"Prerequisites\" class=\"wp-block-list\">\n<li><a href=\"#prerequisites\">Prerequisites<\/a><\/li>\n\n\n\n<li><a href=\"#create-a-local-git-repo-and-separate-codebases\">Create a local git repository and separate codebases<\/a><\/li>\n\n\n\n<li><a href=\"#create-wp-code-for-backend-with-composer\">Create WordPress code for the backend with Composer<\/a><\/li>\n\n\n\n<li><a href=\"#configure-smartsearch\">Create the frontend app<\/a><\/li>\n\n\n\n<li><a href=\"#create-gh-repo\">Create a remote GitHub repository<\/a><\/li>\n\n\n\n<li><a href=\"#configure-nextjs-app-router\">Create GitHub Action and yml file<\/a><\/li>\n\n\n\n<li><a href=\"#push-up-and-test\">Push the code up to your remote repository and test the connection<\/a><\/li>\n\n\n\n<li><a href=\"#env-var\">Conclusion<\/a><\/li>\n<\/ul>\n<\/div>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"prerequisites\">Prerequisites<\/h2>\n\n\n\n<p>Before diving into this guide, ensure you have the following tools and knowledge in place:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Tools Needed<\/strong>:\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/nodejs.org\/\">Node.js and npm<\/a> for managing dependencies and building the frontend.<\/li>\n\n\n\n<li><a href=\"https:\/\/getcomposer.org\/\">Composer<\/a> to handle PHP dependencies for WordPress.<\/li>\n\n\n\n<li><a href=\"https:\/\/git-scm.com\/\">Git<\/a> for version control and managing your monorepo.<\/li>\n\n\n\n<li>A local WordPress development environment (e.g.,<a href=\"https:\/\/localwp.com\/\"> Local<\/a> or an equivalent setup).<\/li>\n\n\n\n<li>A <a href=\"https:\/\/wpengine.com\/headless-wordpress\">WP Engine Headless Platform<\/a> account which will have an existing WP install for you to target from your local machine as well as the frontend node hosting environment.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Basic Knowledge<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Familiarity with WordPress and PHP development.<\/li>\n\n\n\n<li>An understanding of Next.js fundamentals.<\/li>\n\n\n\n<li>An understanding of headless WordPress fundamentals<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<p>Having these prerequisites in place will ensure you can follow the guide smoothly and effectively.&nbsp; If you want to follow the exact file structure and code I use, here is the <a href=\"https:\/\/github.com\/Fran-A-Dev\/monorepo-for-headlessWP\">full repository<\/a> related to this guide.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Steps<\/h2>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"create-a-local-git-repo-and-separate-codebases\">1. Create a local git repository and separate codebases<\/h2>\n\n\n\n<p>In your terminal, navigate to the folder where you want your project to reside. Run the commands below to create and navigate into a new <code> my-monorepo-project<\/code> directory for our project.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span><span class=\"hljs-string\">`mkdir my-monorepo-project`<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">`cd my-monorepo-project`<\/span>\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Initialize a Git repository:<\/p>\n\n\n\n<p><code>`git init`<\/code><\/p>\n\n\n\n<p>This will create a <code>.git<\/code> folder, signaling that Git is now tracking changes in this directory.<\/p>\n\n\n\n<p>Inside your project directory, create two subfolders: one for the frontend and one for the backend:<\/p>\n\n\n\n<p><code>`mkdir frontend backend`<\/code><\/p>\n\n\n\n<p>At this stage, your project structure should look like this:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/lh7-rt.googleusercontent.com\/docsz\/AD_4nXfiOaa0Wm6NRxnUVm-E1rQ2ej4VYkKr29M4Dn0nlhuLgydShCUNVX-BhTblHFQw7bAgtKa7tuychPpNjZOqzESurW27n0BkY6F20DQqyZcNLBLd99wy9wJPMr1ed_l7MOAAGXeOpQ?key=RkeTprl_b2_MbgDI679ZLC_K\" alt=\"\"\/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"create-wp-code-for-backend-with-composer\">2. Create WordPress code for the backend with Composer<\/h2>\n\n\n\n<p>Now that we have our main directories created, let\u2019s start with WordPress and add to the <code>`backend` <\/code>directory using Composer.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">2 a. What is Composer?<\/h3>\n\n\n\n<p>Composer is a dependency manager for PHP that simplifies the process of pulling in libraries and packages. Instead of manually downloading and managing code, Composer lets you specify the dependencies your project needs in a <code>`composer.json`<\/code> file. It then handles downloading those dependencies into a vendor folder and keeps them up to date. This makes your project more modular, easier to maintain and ensures consistency across environments.<\/p>\n\n\n\n<p><br>First, create a <code>`.gitignore`<\/code> at the root of the <code>`backend`<\/code> folder so we can place the files and folders there that we do not want git to push up into our remote repos:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span><span class=\"hljs-string\">`touch .gitignore`<\/span>\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><br><br>Then open up the <code>`.gitignore`<\/code> file you just created and add this code:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span><span class=\"hljs-comment\"># WordPress core and default directories (managed by Composer)<\/span>\n<\/span><\/span><span class='shcb-loc'><span>index.php\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\"># Themes: ignore all themes except our custom demo theme<\/span>\n<\/span><\/span><span class='shcb-loc'><span>wordpress\/wp-content\/themes<span class=\"hljs-comment\">\/*<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">!wordpress\/wp-content\/themes\/demo-theme\/<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\"><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\"># Plugins: ignore all plugins except our custom demo plugin<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">wordpress\/wp-content\/plugins\/*<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">!wordpress\/wp-content\/plugins\/demo-plugin\/<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\"><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\"># Must-use plugins (managed by the platform)<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">wordpress\/wp-content\/mu-plugins\/<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\"><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\"># Uploads directory (dynamically generated content)<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">wordpress\/wp-content\/uploads\/<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\"><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\"># Upgrade files (dynamically generated)<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">wordpress\/wp-content\/upgrade\/<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\"><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\"># Composer-related files and directories<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">vendor\/<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">auth.json<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">.env<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">.env.*<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">!.env.example<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\"><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\"><\/span>\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><\/p>\n\n\n\n<p>In this setup, we\u2019re ignoring the majority of WordPress core and default directories because Composer is responsible for pulling in the specified version of WordPress and placing it inside of the <code>`wordpress`<\/code> directory.&nbsp; Since WP is a dependency of the project and not our own custom code, we don\u2019t want the WP core files in version control.<\/p>\n\n\n\n<p>You should also disable the <strong>\u201cupdate WordPress\u201d <\/strong>buttons in the WP Engine User Portal and the WordPress dashboard. This ensures that the version of WordPress deployed on the server always matches the version specified and locked by Composer, avoiding any unintentional discrepancies. Here is a <a href=\"https:\/\/wpengine.com\/resources\/disable-auto-update\/\">link to the guide<\/a> that explains how to do this.<\/p>\n\n\n\n<p>The <code>`wordpress\/wp-content\/themes\/*` <\/code>and <code>`wordpress\/wp-content\/plugins\/*`<\/code> rules ensure all third-party themes and plugins are excluded from version control. The&nbsp; <code>`!wordpress\/wp-content\/themes\/demo-theme`<\/code> and ` <code>`!wordpress\/wp-content\/plugins\/demo-plugin`<\/code> lines ensure that the \u201cdemo-theme\u201d theme and \u201cdemo-plugin\u201d plugin that contain our project\u2019s custom code are version-controlled.<\/p>\n\n\n\n<p>Upload directories and upgrade files are excluded since these typically contain dynamically generated content that doesn\u2019t belong in version control.&nbsp;<\/p>\n\n\n\n<p>The<code> `\/vendor<\/code>` directory and other Composer-related files like <code>`auth.json`<\/code> are ignored because they can be rebuilt with <code>`composer install`<\/code>. This approach reduces unnecessary clutter in your repository, ensures version control only tracks what you actively manage, and helps maintain a clean and efficient workflow.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>2 b. Configure Composer and Add Dependencies<\/strong><\/h3>\n\n\n\n<p>Inside the backend directory, we\u2019ll set up Composer so that WordPress and any required plugins (like WPGraphQL) are installed into a<code> wordpress\/ <\/code>folder rather than the default vendor folder. This approach keeps your WordPress files in a clearly defined location and helps ensure that core files, plugins, and themes remain neatly organized.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>Initialize Composer<\/strong><\/h4>\n\n\n\n<p>Open your terminal and navigate to the backend folder:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span>cd backend\n<\/span><\/span><span class='shcb-loc'><span>composer init\n<\/span><\/span><\/code><\/span><\/pre>\n\n\n<p>Follow the prompts to create a basic <code>composer.json<\/code>. You can accept defaults for most questions or customize them to your preference.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>Require WordPress as a Project Dependency<\/strong><\/h4>\n\n\n\n<p>Next, add WordPress to your Composer project:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span>composer <span class=\"hljs-built_in\">require<\/span> johnpbloch\/wordpress\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This command updates your newly created <code>composer.json<\/code> file to include WordPress as a dependency and downloads the necessary files.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\"><strong>Add WPGraphQL (Optional but Recommended for Headless)<\/strong><\/h4>\n\n\n\n<p>WPGraphQL is a popular plugin for exposing your WordPress data via GraphQL. Let\u2019s install it using the<a href=\"https:\/\/wpackagist.org\/\"> wpackagist.org<\/a> repository:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span>composer <span class=\"hljs-built_in\">require<\/span> wpackagist-plugin\/wp-graphql\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h4 class=\"wp-block-heading\"><strong>Update the composer.json Configuration<\/strong><\/h4>\n\n\n\n<p>For WordPress to install into <code>backend\/wordpress<\/code> instead of <code>vendor<\/code>, you need to include the John P. Bloch core installer and define custom installer paths. <\/p>\n\n\n\n<p>Below is an example <code>composer.json<\/code> you can use as a reference (feel free to adjust names, versions, and authors as needed):<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"JSON \/ JSON with Comments\" data-shcb-language-slug=\"json\"><span><code class=\"hljs language-json shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span>{\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-attr\">\"name\"<\/span>: <span class=\"hljs-string\">\"myorg\/my-headless-wp\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-attr\">\"version\"<\/span>: <span class=\"hljs-string\">\"1.0.0\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-attr\">\"authors\"<\/span>: &#91;\n<\/span><\/span><span class='shcb-loc'><span>    {\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">\"name\"<\/span>: <span class=\"hljs-string\">\"Your Name\"<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    }\n<\/span><\/span><span class='shcb-loc'><span>  ],\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-attr\">\"require\"<\/span>: {\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-attr\">\"johnpbloch\/wordpress\"<\/span>: <span class=\"hljs-string\">\"^6.7\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-attr\">\"wpackagist-plugin\/wp-graphql\"<\/span>: <span class=\"hljs-string\">\"^2.0\"<\/span>\n<\/span><\/span><span class='shcb-loc'><span>  },\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-attr\">\"minimum-stability\"<\/span>: <span class=\"hljs-string\">\"dev\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-attr\">\"repositories\"<\/span>: &#91;\n<\/span><\/span><span class='shcb-loc'><span>    {\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">\"type\"<\/span>: <span class=\"hljs-string\">\"composer\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">\"url\"<\/span>: <span class=\"hljs-string\">\"https:\/\/wpackagist.org\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">\"only\"<\/span>: &#91;\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-string\">\"wpackagist-plugin\/*\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-string\">\"wpackagist-theme\/*\"<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      ]\n<\/span><\/span><span class='shcb-loc'><span>    }\n<\/span><\/span><span class='shcb-loc'><span>  ],\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-attr\">\"extra\"<\/span>: {\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-attr\">\"installer-paths\"<\/span>: {\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">\"wordpress\/wp-content\/plugins\/{$name}\/\"<\/span>: &#91;\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-string\">\"type:wordpress-plugin\"<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      ],\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">\"wordpress\/wp-content\/themes\/{$name}\/\"<\/span>: &#91;\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-string\">\"type:wordpress-theme\"<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      ]\n<\/span><\/span><span class='shcb-loc'><span>    }\n<\/span><\/span><span class='shcb-loc'><span>  },\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-attr\">\"config\"<\/span>: {\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-attr\">\"allow-plugins\"<\/span>: {\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">\"johnpbloch\/wordpress-core-installer\"<\/span>: <span class=\"hljs-literal\">true<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">\"composer\/installers\"<\/span>: <span class=\"hljs-literal\">true<\/span>\n<\/span><\/span><span class='shcb-loc'><span>    }\n<\/span><\/span><span class='shcb-loc'><span>  }\n<\/span><\/span><span class='shcb-loc'><span>}\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JSON \/ JSON with Comments<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">json<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><strong><code>installer-paths<\/code><\/strong>: Tells Composer to place plugins in wordpress\/wp-content\/plugins\/ and themes in wordpress\/wp-content\/themes\/ instead of the default vendor folder.<\/p>\n\n\n\n<p><strong><code>repositories<\/code><\/strong>: Defines wpackagist.org as a source for WordPress plugins and themes, allowing you to install them via Composer.<\/p>\n\n\n\n<p><strong><code>allow-plugins<\/code><\/strong>: Enables the custom installers needed to place WordPress core in <code>wordpress\/<\/code> and plugins<code>\/themes<\/code> in their correct directories.<\/p>\n\n\n\n<p><strong>Install Dependencies<br><\/strong> Once you\u2019ve finalized your <code>composer.json<\/code> file, install the dependencies:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span>composer install\n<\/span><\/span><\/code><\/span><\/pre>\n\n\n<p><\/p>\n\n\n\n<p>Composer will download WordPress into <code>backend\/wordpress\/<\/code>, along with any specified plugins into <code>backend\/wordpress\/wp-content\/plugins\/<\/code>. For example, WPGraphQL should appear at:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span>backend\/\n<\/span><\/span><span class='shcb-loc'><span>  \u2514\u2500 wordpress\/\n<\/span><\/span><span class='shcb-loc'><span>      \u2514\u2500 wp-content\/\n<\/span><\/span><span class='shcb-loc'><span>          \u2514\u2500 plugins\/\n<\/span><\/span><span class='shcb-loc'><span>              \u2514\u2500 wp-graphql\/\n<\/span><\/span><\/code><\/span><\/pre>\n\n\n<p><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">2 c. Install Composer (if not already installed):<\/h3>\n\n\n\n<p>Before installing Composer, make sure <strong>PHP<\/strong> and <strong>cURL<\/strong> are installed on your system. If you\u2019re missing either, refer to the official documentation for your operating system:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>PHP Installation<\/strong>:<a href=\"https:\/\/www.php.net\/manual\/en\/install.php\"> https:\/\/www.php.net\/manual\/en\/install.php<\/a><\/li>\n\n\n\n<li><strong>cURL Installation<\/strong>: https:\/\/curl.se\/docs\/install.html<\/li>\n<\/ul>\n\n\n\n<p>Once PHP and cURL are installed, you can proceed with installing Composer:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span><span class=\"hljs-string\">`curl -sS https:\/\/getcomposer.org\/installer | php<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">sudo mv composer.phar \/usr\/local\/bin\/composer`<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>2 d. Verify WordPress Installation<\/strong><\/h3>\n\n\n\n<p>After running <code>composer install<\/code>, open your code editor to confirm that WordPress has been successfully installed into the <code>backend\/wordpress <\/code>folder. For example, you should see the following structure:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span>backend\/\n<\/span><\/span><span class='shcb-loc'><span>  \u251c\u2500 .gitignore\n<\/span><\/span><span class='shcb-loc'><span>  \u251c\u2500 composer.json\n<\/span><\/span><span class='shcb-loc'><span>  \u251c\u2500 composer.lock\n<\/span><\/span><span class='shcb-loc'><span>  \u2514\u2500 wordpress\/\n<\/span><\/span><span class='shcb-loc'><span>      \u251c\u2500 wp-admin\/\n<\/span><\/span><span class='shcb-loc'><span>      \u251c\u2500 wp-content\/\n<\/span><\/span><span class='shcb-loc'><span>      \u251c\u2500 wp-includes\/\n<\/span><\/span><span class='shcb-loc'><span>      \u2514\u2500 ...\n<\/span><\/span><\/code><\/span><\/pre>\n\n\n<p>Within <code>wp-content\/plugins<\/code>, you\u2019ll also find any Composer-installed plugins, such as WPGraphQL:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span>backend\/\n<\/span><\/span><span class='shcb-loc'><span>  \u2514\u2500 wordpress\/\n<\/span><\/span><span class='shcb-loc'><span>      \u2514\u2500 wp-content\/\n<\/span><\/span><span class='shcb-loc'><span>          \u2514\u2500 plugins\/\n<\/span><\/span><span class='shcb-loc'><span>              \u2514\u2500 wp-graphql\/\n<\/span><\/span><\/code><\/span><\/pre>\n\n\n<p>You should see this if you open up the code editor:<br><\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"689\" height=\"1024\" src=\"https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2025\/03\/Screenshot-2025-03-18-at-4.04.09\u202fPM-689x1024.png\" alt=\"\" class=\"wp-image-31805\" srcset=\"https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2025\/03\/Screenshot-2025-03-18-at-4.04.09\u202fPM-689x1024.png 689w, https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2025\/03\/Screenshot-2025-03-18-at-4.04.09\u202fPM-202x300.png 202w, https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2025\/03\/Screenshot-2025-03-18-at-4.04.09\u202fPM-768x1141.png 768w, https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2025\/03\/Screenshot-2025-03-18-at-4.04.09\u202fPM-1034x1536.png 1034w, https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2025\/03\/Screenshot-2025-03-18-at-4.04.09\u202fPM.png 1054w\" sizes=\"auto, (max-width: 689px) 100vw, 689px\" \/><\/figure>\n\n\n\n<p>At this stage, you\u2019ve successfully set up Composer in the backend directory, configured your project to install WordPress core and plugins into a dedicated <code>wordpress<\/code> folder, and used <code>.gitignore<\/code> to keep unnecessary files out of version control. You\u2019re now ready to start configuring WordPress and connecting it to the rest of your monorepo setup.  The next thing we need to do is create our frontend.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">3. Create the Frontend App<\/h2>\n\n\n\n<p id=\"create-frontend-app\">Since we are focusing on the monorepo setup in headless, you can go ahead and clone down the repo related to this article here:<\/p>\n\n\n\n<p><a href=\"https:\/\/github.com\/Fran-A-Dev\/monorepo-for-headlessWP\">https:\/\/github.com\/Fran-A-Dev\/monorepo-for-headlessWP<\/a><\/p>\n\n\n\n<p>The example repo provided uses the <a href=\"https:\/\/nextjs.org\/docs\/app\">Next.js App Router<\/a>, but you can apply the concepts of this article to other versions of Next.js, or your preferred frontend framework.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">3 a. Add environment variables<\/h3>\n\n\n\n<p>After the project is created, you\u2019ll need to provide the WordPress GraphQL endpoint so the frontend can fetch data from the backend. Create a <code>.env.local<\/code> file in the frontend directory and add:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span>NEXT_PUBLIC_WPGRAPHQL_ENDPOINT=https:<span class=\"hljs-comment\">\/\/your-wpengine-site.com\/graphql`<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Replace<em> https:\/\/your-wpengine-site.com\/graphql<\/em> with the correct URL of your WordPress install\u2019s GraphQL API.\u00a0\u00a0(Just a reminder, should you choose another frontend, their variables in key and value for your endpoint would differ)<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">3 b. Verify the configuration:<\/h3>\n\n\n\n<p>Open your new frontend project in a code editor and inspect the folders. Ensure that the <code>NEXT_PUBLIC_GRAPHQL_ENDPOINT<\/code> is correctly set by using <code>process.env.NEXT_PUBLIC_GRAPHQL_ENDPOINT<\/code> in your components or data-fetching functions and running<code> `npm run dev`<\/code> in the terminal to ensure that it&#8217;s working on the browser.<\/p>\n\n\n\n<p><br>If you are following along with my repo, I created an example as the home page of the app to display the first 5 post titles and their content from WordPress.&nbsp; Here is the example file from my repo:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span>app\/page.jsx\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">import<\/span> Link <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"next\/link\"<\/span>;\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">import<\/span> { Suspense } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"react\"<\/span>;\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">import<\/span> Loading <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\".\/loading\"<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">async<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">getPosts<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">const<\/span> query = <span class=\"hljs-string\">`<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">  {<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">    posts(first: 5) {<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">      nodes {<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">        title<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">        content<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">        uri<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">      }<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">    }<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">  }<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\">  `<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">const<\/span> res = <span class=\"hljs-keyword\">await<\/span> fetch(\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-string\">`<span class=\"hljs-subst\">${process.env.NEXT_PUBLIC_GRAPHQL_ENDPOINT}<\/span>?query=<span class=\"hljs-subst\">${<span class=\"hljs-built_in\">encodeURIComponent<\/span>(<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\"><span class=\"hljs-subst\">      query<\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-string\"><span class=\"hljs-subst\">    )}<\/span>`<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>    { <span class=\"hljs-attr\">next<\/span>: { <span class=\"hljs-attr\">revalidate<\/span>: <span class=\"hljs-number\">10<\/span> } },\n<\/span><\/span><span class='shcb-loc'><span>    {\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">method<\/span>: <span class=\"hljs-string\">\"GET\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">headers<\/span>: {\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-string\">\"Content-Type\"<\/span>: <span class=\"hljs-string\">\"application\/json\"<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>        <span class=\"hljs-comment\">\/\/ ... any other headers you need to include (like authentication tokens)<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      },\n<\/span><\/span><span class='shcb-loc'><span>    }\n<\/span><\/span><span class='shcb-loc'><span>  );\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">const<\/span> { data } = <span class=\"hljs-keyword\">await<\/span> res.json();\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">return<\/span> data.posts.nodes;\n<\/span><\/span><span class='shcb-loc'><span>}\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-keyword\">async<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">PostList<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">const<\/span> posts = <span class=\"hljs-keyword\">await<\/span> getPosts();\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">return<\/span> (\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Suspense<\/span> <span class=\"hljs-attr\">fallback<\/span>=<span class=\"hljs-string\">{<\/span>&lt;<span class=\"hljs-attr\">Loading<\/span> \/&gt;<\/span>}&gt;<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"xml\"><span class=\"hljs-tag\">      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-tag\">        {posts.map((post) =&gt; (<\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-tag\">          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">key<\/span>=<span class=\"hljs-string\">{post.uri}<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"card\"<\/span>&gt;<\/span><\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\">            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Link<\/span> <span class=\"hljs-attr\">href<\/span>=<span class=\"hljs-string\">{<\/span>`<span class=\"hljs-attr\">post<\/span>${<span class=\"hljs-attr\">post.uri<\/span>}`}&gt;<\/span><\/span><\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\">              <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"border-t border-gray-300 pt-2 mb-2\"<\/span>&gt;<\/span><\/span><\/span><\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\">                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h3<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"mb-2\"<\/span>&gt;<\/span>{post.title}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h3<\/span>&gt;<\/span><\/span><\/span><\/span><\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\">                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h4<\/span>&gt;<\/span>test 10<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h4<\/span>&gt;<\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\">                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\">                  <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"mt-1\"<\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\">                  <span class=\"hljs-attr\">dangerouslySetInnerHTML<\/span>=<span class=\"hljs-string\">{{<\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\">                    <span class=\"hljs-attr\">__html:<\/span> <span class=\"hljs-attr\">post.content.slice<\/span>(<span class=\"hljs-attr\">0<\/span>, <span class=\"hljs-attr\">200<\/span>) + \"<span class=\"hljs-attr\">...<\/span>\",<\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\">                  }}<\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\">                \/&gt;<\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\">              <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\">            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Link<\/span>&gt;<\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\">          <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\">        ))}<\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\">      <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\">    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Suspense<\/span>&gt;<\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\">  );<\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\">}<\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"xml\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><span class=\"hljs-tag\"><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span>\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"create-gh-repo\">4. Create a remote GitHub repository<\/h2>\n\n\n\n<p>Now that we have our frontend and backend code set up, let\u2019s push it to a remote GitHub repo.<\/p>\n\n\n\n<p><span style=\"box-sizing: border-box; margin: 0px; padding: 0px;\">Here are links to<a href=\"https:\/\/docs.github.com\/en\/repositories\/creating-and-managing-repositories\/creating-a-new-repository\" target=\"_blank\">\u00a0creating a new repository<\/a>\u00a0on GitHub and then\u00a0<a href=\"https:\/\/docs.github.com\/en\/get-started\/git-basics\/managing-remote-repositories\" target=\"_blank\">adding it as a remote<\/a>\u00a0for this proje<\/span>ct.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"create-and-configure-gh-action-yml\">5. Create and configure the GitHub Action and <code>`yml` <\/code>file<\/h2>\n\n\n\n<p>Now that our frontend and backend directories are set up, we need to automate the deployment process using GitHub Actions and target our remote WP install on WP Engine.&nbsp;<\/p>\n\n\n\n<p>WP Engine has great documentation and how-to guides for this feature here that you can follow step by step here:<\/p>\n\n\n\n<p><a href=\"https:\/\/wpengine.com\/support\/github-action-deploy\">https:\/\/wpengine.com\/support\/github-action-deploy<\/a><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"push-up-and-test\">6. Push the code up to your remote repository and test the connection<\/h2>\n\n\n\n<p>Now that everything is set up and connected via SSH, it\u2019s time to commit your code and confirm that your GitHub Actions workflow is running properly. Once the workflow completes, you\u2019ll verify the changes on WP Engine\u2019s hosting environment.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">6 a.&nbsp; Stage and commit changes<\/h3>\n\n\n\n<p><br>In your local repository, navigate to the root directory of your project. Remember to add changes to the files we purposely wanted to reflect in our remote WP Engine WP install and GitHub repo. For this article, this included anything on the frontend code that would render in the browser and the custom plugin and theme we created in WordPress.<\/p>\n\n\n\n<p>Run the following commands to stage and commit your backend and frontend changes:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-code-table shcb-line-numbers\"><span class='shcb-loc'><span>git add .\n<\/span><\/span><span class='shcb-loc'><span>git commit -m <span class=\"hljs-string\">\"Initial commit of backend and frontend code\"<\/span>\n<\/span><\/span><span class='shcb-loc'><span>git push origin main\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>We are committing both the backend and frontend directories since they\u2019re housed together in the same repository. This ensures the changes you\u2019ve made to your WordPress files and Next.js app are included.<br><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">6 b. Check the GitHub Actions workflow<\/h3>\n\n\n\n<p>Now that we have pushed our code to our remote GitHub repository, let\u2019s check our GH actions UI to see if it is working properly.<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Open your GitHub repository in the browser.<\/li>\n\n\n\n<li>Go to the <strong>Actions<\/strong> tab and click on it.<\/li>\n<\/ol>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"450\" src=\"https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2025\/03\/Screenshot-2025-03-18-at-4.31.05\u202fPM-1-1024x450.png\" alt=\"\" class=\"wp-image-31808\" srcset=\"https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2025\/03\/Screenshot-2025-03-18-at-4.31.05\u202fPM-1-1024x450.png 1024w, https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2025\/03\/Screenshot-2025-03-18-at-4.31.05\u202fPM-1-300x132.png 300w, https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2025\/03\/Screenshot-2025-03-18-at-4.31.05\u202fPM-1-768x338.png 768w, https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2025\/03\/Screenshot-2025-03-18-at-4.31.05\u202fPM-1-1536x675.png 1536w, https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2025\/03\/Screenshot-2025-03-18-at-4.31.05\u202fPM-1-2048x901.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p><br>3. You should see a running workflow for the deployment process. Click on it to view the logs and ensure everything runs without errors.<\/p>\n\n\n\n<p>4. If you see a green checkmark, your code has been successfully deployed to WP Engine.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"555\" src=\"https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2025\/03\/Screenshot-2025-03-18-at-4.39.33\u202fPM-1024x555.png\" alt=\"\" class=\"wp-image-31810\" srcset=\"https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2025\/03\/Screenshot-2025-03-18-at-4.39.33\u202fPM-1024x555.png 1024w, https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2025\/03\/Screenshot-2025-03-18-at-4.39.33\u202fPM-300x163.png 300w, https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2025\/03\/Screenshot-2025-03-18-at-4.39.33\u202fPM-768x416.png 768w, https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2025\/03\/Screenshot-2025-03-18-at-4.39.33\u202fPM-1536x832.png 1536w, https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2025\/03\/Screenshot-2025-03-18-at-4.39.33\u202fPM-2048x1109.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"555\" src=\"https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2025\/03\/Screenshot-2025-03-18-at-4.39.33\u202fPM-1-1024x555.png\" alt=\"\" class=\"wp-image-31812\" srcset=\"https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2025\/03\/Screenshot-2025-03-18-at-4.39.33\u202fPM-1-1024x555.png 1024w, https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2025\/03\/Screenshot-2025-03-18-at-4.39.33\u202fPM-1-300x163.png 300w, https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2025\/03\/Screenshot-2025-03-18-at-4.39.33\u202fPM-1-768x416.png 768w, https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2025\/03\/Screenshot-2025-03-18-at-4.39.33\u202fPM-1-1536x832.png 1536w, https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2025\/03\/Screenshot-2025-03-18-at-4.39.33\u202fPM-1-2048x1109.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">6 c. Verify changes on WP Engine\u2019s headless WordPress environment<\/h3>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Log in to the WP Engine User Portal and navigate to your WordPress environment.<\/li>\n\n\n\n<li>Open your site in the browser and confirm that the changes you made such as new or updated content, custom plugins, or themes &#8211; are reflected.<\/li>\n\n\n\n<li>If you\u2019re using a custom headless frontend, check that your site is showing the correct data from your WordPress backend.<\/li>\n<\/ol>\n\n\n\n<p>If you are new to the WP Engine user portal that features the headless WP hosting platform along with your WP installs <em>(both headless and traditional) <\/em>please refer to their documentation on navigating the user portal here:<br><br><a href=\"https:\/\/developers.wpengine.com\/docs\/atlas\/getting-started\/create-app\/\">https:\/\/developers.wpengine.com\/docs\/atlas\/getting-started\/create-app\/<\/a><\/p>\n\n\n\n<p><a href=\"https:\/\/wpengine.com\/support\/wp-engine-user-portal\">https:\/\/wpengine.com\/support\/wp-engine-user-portal<\/a><\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"conclusion\">Conclusion&nbsp;<\/h2>\n\n\n\n<p>Headless WordPress and a monorepo setup can get a bit daunting and confusing.&nbsp; Fortunately, WP Engine has tools on their platform to make deployment easier on both your frontend and backend, especially when you house it in one repo.&nbsp; We hope you have a better understanding of how to do so with this article.<\/p>\n\n\n\n<p>As always, we\u2019re super stoked to hear your feedback and learn about the headless projects you\u2019re working on, so hit us up in our <a href=\"https:\/\/discord.com\/invite\/headless-wordpress-836253505944813629\">Discord<\/a>!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In a decoupled environment, maintaining a streamlined workflow is key to creating and managing effective apps and sites. Combining WordPress with a modern JavaScript framework like Next.js in a monorepo [&hellip;]<\/p>\n","protected":false},"author":20,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_EventAllDay":false,"_EventTimezone":"","_EventStartDate":"","_EventEndDate":"","_EventStartDateUTC":"","_EventEndDateUTC":"","_EventShowMap":false,"_EventShowMapLink":false,"_EventURL":"","_EventCost":"","_EventCostDescription":"","_EventCurrencySymbol":"","_EventCurrencyCode":"","_EventCurrencyPosition":"","_EventDateTimeSeparator":"","_EventTimeRangeSeparator":"","_EventOrganizerID":[],"_EventVenueID":[],"_OrganizerEmail":"","_OrganizerPhone":"","_OrganizerWebsite":"","_VenueAddress":"","_VenueCity":"","_VenueCountry":"","_VenueProvince":"","_VenueState":"","_VenueZip":"","_VenuePhone":"","_VenueURL":"","_VenueStateProvince":"","_VenueLat":"","_VenueLng":"","_VenueShowMap":false,"_VenueShowMapLink":false,"footnotes":""},"categories":[23],"tags":[],"class_list":["post-31802","post","type-post","status-publish","format-standard","hentry","category-headless"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.5 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Headless WordPress Monorepo Setup - Builders<\/title>\n<meta name=\"description\" content=\"Learn how to set up a Headless WordPress monorepo with best practices for managing dual codebases efficiently. Streamline your workflow using WP Engine&#039;s GitHub Actions.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/wpengine.com\/builders\/headless-wordpress-monorepo-setup\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"\ud83d\ude80 Headless WordPress Monorepo Setup on WP Engine\" \/>\n<meta property=\"og:description\" content=\"Learn how to set up headless WordPress with a monorepo on WP Engine! Discover how to manage dual codebases efficiently using tools like WP Engine GitHub Actions, WPGraphQL, and Composer. \u26a1 #HeadlessWordPress #Monorepo\" \/>\n<meta property=\"og:url\" content=\"https:\/\/wpengine.com\/builders\/headless-wordpress-monorepo-setup\/\" \/>\n<meta property=\"og:site_name\" content=\"Builders\" \/>\n<meta property=\"article:published_time\" content=\"2025-03-19T18:18:14+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-03-19T18:18:16+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2025\/03\/WPE-Builders-Contributing-to-\u2028Open-Source-Projects-1920x1080@2x-1024x576.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1024\" \/>\n\t<meta property=\"og:image:height\" content=\"576\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Francis Agulto\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@wpebuilders\" \/>\n<meta name=\"twitter:site\" content=\"@wpebuilders\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Francis Agulto\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"10 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/headless-wordpress-monorepo-setup\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/headless-wordpress-monorepo-setup\\\/\"},\"author\":{\"name\":\"Francis Agulto\",\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/#\\\/schema\\\/person\\\/bcdcb4ac0b215c34b6b30e440a24dc54\"},\"headline\":\"Headless WordPress Monorepo Setup\",\"datePublished\":\"2025-03-19T18:18:14+00:00\",\"dateModified\":\"2025-03-19T18:18:16+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/headless-wordpress-monorepo-setup\\\/\"},\"wordCount\":2018,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/headless-wordpress-monorepo-setup\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/lh7-rt.googleusercontent.com\\\/docsz\\\/AD_4nXfiOaa0Wm6NRxnUVm-E1rQ2ej4VYkKr29M4Dn0nlhuLgydShCUNVX-BhTblHFQw7bAgtKa7tuychPpNjZOqzESurW27n0BkY6F20DQqyZcNLBLd99wy9wJPMr1ed_l7MOAAGXeOpQ?key=RkeTprl_b2_MbgDI679ZLC_K\",\"articleSection\":[\"Headless\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/wpengine.com\\\/builders\\\/headless-wordpress-monorepo-setup\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/headless-wordpress-monorepo-setup\\\/\",\"url\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/headless-wordpress-monorepo-setup\\\/\",\"name\":\"Headless WordPress Monorepo Setup - Builders\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/headless-wordpress-monorepo-setup\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/headless-wordpress-monorepo-setup\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/lh7-rt.googleusercontent.com\\\/docsz\\\/AD_4nXfiOaa0Wm6NRxnUVm-E1rQ2ej4VYkKr29M4Dn0nlhuLgydShCUNVX-BhTblHFQw7bAgtKa7tuychPpNjZOqzESurW27n0BkY6F20DQqyZcNLBLd99wy9wJPMr1ed_l7MOAAGXeOpQ?key=RkeTprl_b2_MbgDI679ZLC_K\",\"datePublished\":\"2025-03-19T18:18:14+00:00\",\"dateModified\":\"2025-03-19T18:18:16+00:00\",\"description\":\"Learn how to set up a Headless WordPress monorepo with best practices for managing dual codebases efficiently. Streamline your workflow using WP Engine's GitHub Actions.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/headless-wordpress-monorepo-setup\\\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/wpengine.com\\\/builders\\\/headless-wordpress-monorepo-setup\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/headless-wordpress-monorepo-setup\\\/#primaryimage\",\"url\":\"https:\\\/\\\/lh7-rt.googleusercontent.com\\\/docsz\\\/AD_4nXfiOaa0Wm6NRxnUVm-E1rQ2ej4VYkKr29M4Dn0nlhuLgydShCUNVX-BhTblHFQw7bAgtKa7tuychPpNjZOqzESurW27n0BkY6F20DQqyZcNLBLd99wy9wJPMr1ed_l7MOAAGXeOpQ?key=RkeTprl_b2_MbgDI679ZLC_K\",\"contentUrl\":\"https:\\\/\\\/lh7-rt.googleusercontent.com\\\/docsz\\\/AD_4nXfiOaa0Wm6NRxnUVm-E1rQ2ej4VYkKr29M4Dn0nlhuLgydShCUNVX-BhTblHFQw7bAgtKa7tuychPpNjZOqzESurW27n0BkY6F20DQqyZcNLBLd99wy9wJPMr1ed_l7MOAAGXeOpQ?key=RkeTprl_b2_MbgDI679ZLC_K\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/headless-wordpress-monorepo-setup\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Headless WordPress Monorepo Setup\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/#website\",\"url\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/\",\"name\":\"Builders\",\"description\":\"Reimagining the way we build with WordPress.\",\"publisher\":{\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/#organization\",\"name\":\"WP Engine\",\"url\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/wp-content\\\/uploads\\\/2024\\\/05\\\/WP-Engine-Horizontal@2x.png\",\"contentUrl\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/wp-content\\\/uploads\\\/2024\\\/05\\\/WP-Engine-Horizontal@2x.png\",\"width\":348,\"height\":68,\"caption\":\"WP Engine\"},\"image\":{\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/#\\\/schema\\\/logo\\\/image\\\/\"},\"sameAs\":[\"https:\\\/\\\/x.com\\\/wpebuilders\",\"https:\\\/\\\/www.youtube.com\\\/channel\\\/UCh1WuL54XFb9ZI6m6goFv1g\"]},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/#\\\/schema\\\/person\\\/bcdcb4ac0b215c34b6b30e440a24dc54\",\"name\":\"Francis Agulto\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/0c8a05c76944fc987d57296c96dc368055844527088c0aa44297edbfa8b82546?s=96&d=mm&r=g\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/0c8a05c76944fc987d57296c96dc368055844527088c0aa44297edbfa8b82546?s=96&d=mm&r=g\",\"contentUrl\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/0c8a05c76944fc987d57296c96dc368055844527088c0aa44297edbfa8b82546?s=96&d=mm&r=g\",\"caption\":\"Francis Agulto\"},\"description\":\"Fran Agulto is a Developer Advocate at WP Engine. He is a lover of all things headless WordPress, Rock Climbing, and overall being stoked for people that love what they do and share that stoke with others! Follow me on Twitter for cool stoked headless WP!\",\"url\":\"https:\\\/\\\/wpengine.com\\\/builders\\\/author\\\/francis-agultowpengine-com-2-2-2-2-2-2-2-2-2-2-2-3\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Headless WordPress Monorepo Setup - Builders","description":"Learn how to set up a Headless WordPress monorepo with best practices for managing dual codebases efficiently. Streamline your workflow using WP Engine's GitHub Actions.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/wpengine.com\/builders\/headless-wordpress-monorepo-setup\/","og_locale":"en_US","og_type":"article","og_title":"\ud83d\ude80 Headless WordPress Monorepo Setup on WP Engine","og_description":"Learn how to set up headless WordPress with a monorepo on WP Engine! Discover how to manage dual codebases efficiently using tools like WP Engine GitHub Actions, WPGraphQL, and Composer. \u26a1 #HeadlessWordPress #Monorepo","og_url":"https:\/\/wpengine.com\/builders\/headless-wordpress-monorepo-setup\/","og_site_name":"Builders","article_published_time":"2025-03-19T18:18:14+00:00","article_modified_time":"2025-03-19T18:18:16+00:00","og_image":[{"width":1024,"height":576,"url":"https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2025\/03\/WPE-Builders-Contributing-to-\u2028Open-Source-Projects-1920x1080@2x-1024x576.png","type":"image\/png"}],"author":"Francis Agulto","twitter_card":"summary_large_image","twitter_creator":"@wpebuilders","twitter_site":"@wpebuilders","twitter_misc":{"Written by":"Francis Agulto","Est. reading time":"10 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/wpengine.com\/builders\/headless-wordpress-monorepo-setup\/#article","isPartOf":{"@id":"https:\/\/wpengine.com\/builders\/headless-wordpress-monorepo-setup\/"},"author":{"name":"Francis Agulto","@id":"https:\/\/wpengine.com\/builders\/#\/schema\/person\/bcdcb4ac0b215c34b6b30e440a24dc54"},"headline":"Headless WordPress Monorepo Setup","datePublished":"2025-03-19T18:18:14+00:00","dateModified":"2025-03-19T18:18:16+00:00","mainEntityOfPage":{"@id":"https:\/\/wpengine.com\/builders\/headless-wordpress-monorepo-setup\/"},"wordCount":2018,"commentCount":0,"publisher":{"@id":"https:\/\/wpengine.com\/builders\/#organization"},"image":{"@id":"https:\/\/wpengine.com\/builders\/headless-wordpress-monorepo-setup\/#primaryimage"},"thumbnailUrl":"https:\/\/lh7-rt.googleusercontent.com\/docsz\/AD_4nXfiOaa0Wm6NRxnUVm-E1rQ2ej4VYkKr29M4Dn0nlhuLgydShCUNVX-BhTblHFQw7bAgtKa7tuychPpNjZOqzESurW27n0BkY6F20DQqyZcNLBLd99wy9wJPMr1ed_l7MOAAGXeOpQ?key=RkeTprl_b2_MbgDI679ZLC_K","articleSection":["Headless"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/wpengine.com\/builders\/headless-wordpress-monorepo-setup\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/wpengine.com\/builders\/headless-wordpress-monorepo-setup\/","url":"https:\/\/wpengine.com\/builders\/headless-wordpress-monorepo-setup\/","name":"Headless WordPress Monorepo Setup - Builders","isPartOf":{"@id":"https:\/\/wpengine.com\/builders\/#website"},"primaryImageOfPage":{"@id":"https:\/\/wpengine.com\/builders\/headless-wordpress-monorepo-setup\/#primaryimage"},"image":{"@id":"https:\/\/wpengine.com\/builders\/headless-wordpress-monorepo-setup\/#primaryimage"},"thumbnailUrl":"https:\/\/lh7-rt.googleusercontent.com\/docsz\/AD_4nXfiOaa0Wm6NRxnUVm-E1rQ2ej4VYkKr29M4Dn0nlhuLgydShCUNVX-BhTblHFQw7bAgtKa7tuychPpNjZOqzESurW27n0BkY6F20DQqyZcNLBLd99wy9wJPMr1ed_l7MOAAGXeOpQ?key=RkeTprl_b2_MbgDI679ZLC_K","datePublished":"2025-03-19T18:18:14+00:00","dateModified":"2025-03-19T18:18:16+00:00","description":"Learn how to set up a Headless WordPress monorepo with best practices for managing dual codebases efficiently. Streamline your workflow using WP Engine's GitHub Actions.","breadcrumb":{"@id":"https:\/\/wpengine.com\/builders\/headless-wordpress-monorepo-setup\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/wpengine.com\/builders\/headless-wordpress-monorepo-setup\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/wpengine.com\/builders\/headless-wordpress-monorepo-setup\/#primaryimage","url":"https:\/\/lh7-rt.googleusercontent.com\/docsz\/AD_4nXfiOaa0Wm6NRxnUVm-E1rQ2ej4VYkKr29M4Dn0nlhuLgydShCUNVX-BhTblHFQw7bAgtKa7tuychPpNjZOqzESurW27n0BkY6F20DQqyZcNLBLd99wy9wJPMr1ed_l7MOAAGXeOpQ?key=RkeTprl_b2_MbgDI679ZLC_K","contentUrl":"https:\/\/lh7-rt.googleusercontent.com\/docsz\/AD_4nXfiOaa0Wm6NRxnUVm-E1rQ2ej4VYkKr29M4Dn0nlhuLgydShCUNVX-BhTblHFQw7bAgtKa7tuychPpNjZOqzESurW27n0BkY6F20DQqyZcNLBLd99wy9wJPMr1ed_l7MOAAGXeOpQ?key=RkeTprl_b2_MbgDI679ZLC_K"},{"@type":"BreadcrumbList","@id":"https:\/\/wpengine.com\/builders\/headless-wordpress-monorepo-setup\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/wpengine.com\/builders\/"},{"@type":"ListItem","position":2,"name":"Headless WordPress Monorepo Setup"}]},{"@type":"WebSite","@id":"https:\/\/wpengine.com\/builders\/#website","url":"https:\/\/wpengine.com\/builders\/","name":"Builders","description":"Reimagining the way we build with WordPress.","publisher":{"@id":"https:\/\/wpengine.com\/builders\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/wpengine.com\/builders\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/wpengine.com\/builders\/#organization","name":"WP Engine","url":"https:\/\/wpengine.com\/builders\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/wpengine.com\/builders\/#\/schema\/logo\/image\/","url":"https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2024\/05\/WP-Engine-Horizontal@2x.png","contentUrl":"https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2024\/05\/WP-Engine-Horizontal@2x.png","width":348,"height":68,"caption":"WP Engine"},"image":{"@id":"https:\/\/wpengine.com\/builders\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/x.com\/wpebuilders","https:\/\/www.youtube.com\/channel\/UCh1WuL54XFb9ZI6m6goFv1g"]},{"@type":"Person","@id":"https:\/\/wpengine.com\/builders\/#\/schema\/person\/bcdcb4ac0b215c34b6b30e440a24dc54","name":"Francis Agulto","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/secure.gravatar.com\/avatar\/0c8a05c76944fc987d57296c96dc368055844527088c0aa44297edbfa8b82546?s=96&d=mm&r=g","url":"https:\/\/secure.gravatar.com\/avatar\/0c8a05c76944fc987d57296c96dc368055844527088c0aa44297edbfa8b82546?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/0c8a05c76944fc987d57296c96dc368055844527088c0aa44297edbfa8b82546?s=96&d=mm&r=g","caption":"Francis Agulto"},"description":"Fran Agulto is a Developer Advocate at WP Engine. He is a lover of all things headless WordPress, Rock Climbing, and overall being stoked for people that love what they do and share that stoke with others! Follow me on Twitter for cool stoked headless WP!","url":"https:\/\/wpengine.com\/builders\/author\/francis-agultowpengine-com-2-2-2-2-2-2-2-2-2-2-2-3\/"}]}},"_links":{"self":[{"href":"https:\/\/wpengine.com\/builders\/wp-json\/wp\/v2\/posts\/31802","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wpengine.com\/builders\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/wpengine.com\/builders\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/wpengine.com\/builders\/wp-json\/wp\/v2\/users\/20"}],"replies":[{"embeddable":true,"href":"https:\/\/wpengine.com\/builders\/wp-json\/wp\/v2\/comments?post=31802"}],"version-history":[{"count":0,"href":"https:\/\/wpengine.com\/builders\/wp-json\/wp\/v2\/posts\/31802\/revisions"}],"wp:attachment":[{"href":"https:\/\/wpengine.com\/builders\/wp-json\/wp\/v2\/media?parent=31802"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/wpengine.com\/builders\/wp-json\/wp\/v2\/categories?post=31802"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/wpengine.com\/builders\/wp-json\/wp\/v2\/tags?post=31802"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}