{"id":31946,"date":"2025-08-04T17:33:27","date_gmt":"2025-08-04T22:33:27","guid":{"rendered":"https:\/\/wpengine.com\/builders\/?p=31946"},"modified":"2025-08-07T15:37:45","modified_gmt":"2025-08-07T20:37:45","slug":"next-js-wordpress-routing-and-graphql","status":"publish","type":"post","link":"https:\/\/wpengine.com\/builders\/next-js-wordpress-routing-and-graphql\/","title":{"rendered":"Next.js + WordPress: Routing and GraphQL"},"content":{"rendered":"\n<p><a href=\"https:\/\/nextjs.org\/\" target=\"_blank\" rel=\"noreferrer noopener\">Next.js<\/a> is one of the most popular front-ends for building with <a href=\"https:\/\/wpengine.com\/resources\/headless-cms-and-wordpress\/\" target=\"_blank\" rel=\"noreferrer noopener\">headless WordPress<\/a>. My Reddit notifications are littered with Next.js + headless WordPress recommendations. Today, we\u2019re going to look at implementing routing and data fetching for headless WordPress with Next.js.<\/p>\n\n\n\n<p>You may wonder why we\u2019re covering this, since WP Engine is behind <a href=\"https:\/\/faustjs.org\/\" target=\"_blank\" rel=\"noreferrer noopener\">Faust.js<\/a>, which provides its own routing solution for headless WordPress + Next.js sites. Faust\u2019s routing solution isn\u2019t perfect, however. In this article, we\u2019ll experiment with another approach that offers improvements. We\u2019ll be working with the <a href=\"https:\/\/nextjs.org\/docs\/pages\" target=\"_blank\" rel=\"noreferrer noopener\">Pages Router<\/a>, though many of the concepts could be translated to the <a href=\"https:\/\/nextjs.org\/docs\/app\" target=\"_blank\" rel=\"noreferrer noopener\">App Router<\/a>.<\/p>\n\n\n\n<p>The two major issues we\u2019ll be looking at today are bundle splitting and query optimization. Currently, the catch-all route doesn\u2019t bundle template code separately. While this might only cause a couple of KB of bloat on small sites, the more complexity you add means you might be loading 10-100 KBs of extra code on every route.\u00a0<\/p>\n\n\n\n<p>When Faust was first conceived years ago, I don&#8217;t think the team fully understood the importance of small queries. Because of this, Faust\u2019s main mechanism for querying GraphQL only allows for one query per template. We have since learned that this is an antipattern. Just because you can query everything you need from GraphQL in one request doesn\u2019t mean you should. In this post, we\u2019ll also experiment with alternative ways to handle data fetching.\u00a0<\/p>\n\n\n\n<p>For a working example of what we discuss here, check out the <a href=\"https:\/\/github.com\/wpengine\/hwptoolkit\/tree\/main\/examples\/next\/template-hierarchy\/\">wpengine\/hwptoolkit<\/a> repo.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p><strong>Note<\/strong>: We <a href=\"https:\/\/github.com\/wpengine\/faustjs\/discussions\/2140\">recently announced<\/a> that we\u2019re working on improving Faust. The work I did for this article and much more is going into improving Faust.<\/p>\n<\/blockquote>\n\n\n\n<h2 class=\"wp-block-heading\">Routing<\/h2>\n\n\n\n<p>In the <a href=\"https:\/\/wpengine.com\/builders\/astro-wordpress-routing-and-graphql\/#basics-of-the-template-hierarchy\">article on Astro<\/a>, we discussed four major steps in the template hierarchy that must be recreated for a front-end framework. URI => Data => Template => Render: Data + Template.<\/p>\n\n\n\n<p>In our article on <a href=\"https:\/\/wpengine.com\/builders\/sveltekit-wordpress-routing-and-graphql\/\">SvelteKit<\/a>, we experimented with new routing methods due to its implementation details. Next is similar in that middleware and rewrites just won\u2019t work for us.&nbsp; However, unlike SvelteKit, Next doesn\u2019t have a way to load components outside of components.&nbsp;<\/p>\n\n\n\n<p><span style=\"box-sizing: border-box; margin: 0px; padding: 0px;\">Ne<\/span>xt.js&nbsp;does have the ability to&nbsp;dynamically import&nbsp;components, which will solve our bundling issue. Our template loader will only dynamically import the needed template, not all templates.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Template Hierarchy in Next.js<\/h2>\n\n\n\n<p>Let\u2019s put this all together in Next.js. The steps are:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Get the URI<\/li>\n\n\n\n<li>Determine the template\n<ul class=\"wp-block-list\">\n<li>Make a \u201cseed query\u201d to WordPress to fetch template data<\/li>\n\n\n\n<li>Collect available templates for rendering<\/li>\n\n\n\n<li>Calculate possible templates the data could use<\/li>\n\n\n\n<li>Figure out which of the available templates to use based on the prioritized order of most to least specific possible templates<\/li>\n\n\n\n<li>Use the dynamically imported template<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>Fetch more data from WordPress to actually render the template<\/li>\n\n\n\n<li>Merge the selected template and data for rendering<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">Catch-All Route<\/h3>\n\n\n\n<p>To get the full <code>URI<\/code>, we\u2019ll use Next\u2019s file-system router and <em>optional<\/em> <a href=\"https:\/\/nextjs.org\/docs\/pages\/building-your-application\/routing\/dynamic-routes#optional-catch-all-segments\">catch-all route<\/a>: <code>src\/pages\/[[...uri]].js<\/code>.&nbsp;<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p><strong>Note<\/strong>: The <code>[...uri].js<\/code> pattern may be more common, but&nbsp; it requires a value for <code>uri<\/code>. This means root (<code>\/<\/code>) routes aren\u2019t included. This is commonly not understood, and folks also include an <code>index.js<\/code> to handle this usecase. However, the double brackets make <code>uri<\/code> optional and thus inclusive of <code>\/<\/code>. This <code>undefined<\/code> value will need to be handled later.<\/p>\n<\/blockquote>\n\n\n\n<h3 class=\"wp-block-heading\">Seed Query<\/h3>\n\n\n\n<p><span style=\"box-sizing: border-box; margin: 0px; padding: 0px;\">In the Next Pages Router, al<\/span>l server-side queries will need to be executed in&nbsp;<code>getStaticProps<\/code>&nbsp;or, more commonly,&nbsp;<code>getServerSideProps<\/code>;&nbsp;either way, this will be in the <code>src\/pages\/[[...uri]].js<\/code> route.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Calculating Possible Templates<\/h3>\n\n\n\n<p>Our app will use a <a href=\"https:\/\/github.com\/wpengine\/hwptoolkit\/blob\/main\/examples\/sveltekit\/template-hierarchy-data-fetching-urql\/example-app\/src\/lib\/templates.ts#L30-L166\">function we built for Faust<\/a> to take the data from the seed query and generate a list of possible templates, sorted from most specific to least specific. For example, the templates for a page could look like this: <code>[page-sample-page, page-2, page, singular, index]<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Creating Available Templates<\/h3>\n\n\n\n<p>Because we\u2019re using dynamic imports to import our WordPress templates, they don\u2019t have to be dedicated routes. However, we do need a single location where they all exist so we can easily import them programmatically. We will use a <code>wp-templates<\/code> directory with our templates inside, like this:<\/p>\n\n\n\n<p>src<\/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>src\n<\/span><\/span><span class='shcb-loc'><span>\u00a0 \u21b3 wp-templates\/\n<\/span><\/span><span class='shcb-loc'><span>    \u21b3 index.js\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0\u00a0 \u21b3 <span class=\"hljs-keyword\">default<\/span>.js\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0\u00a0 \u21b3 home.js\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0\u00a0\u00a0\u21b3 archive.js\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0\u00a0\u00a0\u21b3 single.js\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><\/p>\n\n\n\n<p>With Astro and SvelteKit, I opted to read these from the file system to avoid having to import individual templates manually. Unfortunately, Next won\u2019t allow us to do this. Because of the limitations in Next\u2019s bundler and how <code>next\/dynamic<\/code> Works, they make it clear in the <a href=\"https:\/\/nextjs.org\/docs\/pages\/guides\/lazy-loading#examples\">documentation<\/a> that variables can\u2019t be used; static strings are required!<\/p>\n\n\n\n<p>This means we use <code>index.js<\/code> in our <code>wp-templates<\/code> folder to handle dynamically importing the individual templates and exporting them into key-value pairs, where the keys are the expected WP template names. In our example above, this is mostly 1-to-1, though <code>default.js<\/code> will become <code>index<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Choosing a template<\/h3>\n\n\n\n<p>We now have a list of possible templates and a list of available templates. Based on the prioritized list of possible templates, we can determine which of the available templates to use.&nbsp;<\/p>\n\n\n\n<p>A <a href=\"https:\/\/github.com\/wpengine\/hwptoolkit\/blob\/main\/examples\/next\/template-hierarchy\/example-app\/src\/lib\/templates.js#L165-L177\" target=\"_blank\" rel=\"noreferrer noopener\">quick bit of JavaScript<\/a> can compare the list of possible templates <code>[single-post-sample-post, single-post, single, singular, index]<\/code> to the list of available templates <code>[archive, home, archive, single]<\/code> and the first match is our template. In this case, <code>single<\/code> is the winner!<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Putting it all together<\/h3>\n\n\n\n<p>Now that we\u2019ve built all the pieces, we can make a single function that takes a URI and returns the template. The <code>getServerSideProps<\/code> function of our catch-all route now looks something like this:<\/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-comment\">\/\/ src\/pages\/&#91;&#91;...uri]].js<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">import<\/span> { uriToTemplate } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"@\/lib\/templateHierarchy\"<\/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\">async<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">getServerSideProps<\/span>(<span class=\"hljs-params\">context<\/span>) <\/span>{\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> { params } = context;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> uri = <span class=\"hljs-built_in\">Array<\/span>.isArray(params.uri)\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0\u00a0\u00a0? <span class=\"hljs-string\">\"\/\"<\/span> + params.uri.join(<span class=\"hljs-string\">\"\/\"<\/span>) + <span class=\"hljs-string\">\"\/\"<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0\u00a0\u00a0: <span class=\"hljs-string\">\"\/\"<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> templateData = <span class=\"hljs-keyword\">await<\/span> uriToTemplate({ uri });\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0<span class=\"hljs-keyword\">if<\/span> (\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0\u00a0\u00a0!templateData?.template?.id ||\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0\u00a0\u00a0templateData?.template?.id === <span class=\"hljs-string\">\"404 Not Found\"<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0) {\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">return<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">notFound<\/span>: <span class=\"hljs-literal\">true<\/span>,\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0\u00a0\u00a0};\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0}\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0<span class=\"hljs-keyword\">return<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">props<\/span>: {\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0uri,\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-comment\">\/\/ https:\/\/github.com\/vercel\/next.js\/discussions\/11209#discussioncomment-35915<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">templateData<\/span>: <span class=\"hljs-built_in\">JSON<\/span>.parse(<span class=\"hljs-built_in\">JSON<\/span>.stringify(templateData)),\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0\u00a0\u00a0},\n<\/span><\/span><span class='shcb-loc'><span>\u00a0\u00a0};\n<\/span><\/span><span class='shcb-loc'><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<h3 class=\"wp-block-heading\">Loading the Template<\/h3>\n\n\n\n<p>Loading templates is done manually in the wp-templates\/index.js. That will look something like:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" 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-comment\">\/\/ src\/wp-templates\/index.js<\/span>\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">import<\/span> dynamic <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"next\/dynamic\"<\/span>;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">const<\/span> home = dynamic(<span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> <span class=\"hljs-keyword\">import<\/span>(<span class=\"hljs-string\">\".\/home.js\"<\/span>), {\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-attr\">loading<\/span>: <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>Loading Home Template...<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span><\/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> index = dynamic(<span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> <span class=\"hljs-keyword\">import<\/span>(<span class=\"hljs-string\">\".\/default.js\"<\/span>), {\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-attr\">loading<\/span>: <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>Loading Index Template...<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span><\/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> single = dynamic(<span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> <span class=\"hljs-keyword\">import<\/span>(<span class=\"hljs-string\">\".\/single.js\"<\/span>), {\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-attr\">loading<\/span>: <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span>&gt;<\/span>Loading Single Template...<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span><\/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\">export<\/span> <span class=\"hljs-keyword\">default<\/span> { home, index, single };\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\">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<h3 class=\"wp-block-heading\">Rendering the template<\/h3>\n\n\n\n<p>Okay! Our <code>getServerSideProps<\/code> function does the hard work of figuring out which template to render and loading the seed query. Now, in our page component, we can handle rendering the template.\u00a0<\/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><span class=\"hljs-comment\">\/\/ src\/pages\/&#91;&#91;...uri]].js<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">import<\/span> availableTemplates <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"@\/wp-templates\"<\/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-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">Page<\/span>(<span class=\"hljs-params\">props<\/span>) <\/span>{\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">const<\/span> { templateData } = props;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">const<\/span> PageTemplate = availableTemplates&#91;templateData.template?.id];\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\">PageTemplate<\/span> {<span class=\"hljs-attr\">...props<\/span>} \/&gt;<\/span><\/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-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<h2 class=\"wp-block-heading\">Querying Data<\/h2>\n\n\n\n<p>Now that we have a working router, let\u2019s turn to fetching data for our templates. Currently, Faust\u2019s main mechanism is <code>query<\/code> and <code>variables<\/code> exports from a given template. These are handled upstream in the catch-all routes <code>get____Props<\/code> function.&nbsp;<\/p>\n\n\n\n<p>As mentioned previously, we want to improve this by allowing multiple queries per template. Faust started to implement this by allowing a <code>queries<\/code> export. Without getting into too many details, this implementation has its own set of problems. We were able to implement this same pattern in the SvelteKit example without much difficulty and avoided many of the issues. Let\u2019s do the same here.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Defining Queries<\/h3>\n\n\n\n<p>While a full implementation might need some more advanced features, we\u2019re going to keep ours fairly simple to start.&nbsp;<\/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>Component.queries = &#91;\n<\/span><\/span><span class='shcb-loc'><span>  {\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-attr\">name<\/span>: myQuery,\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-attr\">query<\/span>: gql<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-attr\">variables<\/span>: <span class=\"hljs-function\">(<span class=\"hljs-params\">_context, { uri }<\/span>) =&gt;<\/span> ({ uri })\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-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<p><\/p>\n\n\n\n<p>Instead of relying on complex hash algorithms to identify our queries, we\u2019re going to use simple names. The GraphQL query name is used as a fallback if one is not provided. However, if you\u2019re running one query with different variables, you may need to give it a unique name, so we provide the name field.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Executing Queries<\/h3>\n\n\n\n<p>In our <code>getServerSideProps<\/code> function, we\u2019re already handling the loading of our template. Now, we can access this <code>queries<\/code> array from there and execute our queries. Initially, I thought this would look something like:&nbsp;<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" 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-keyword\">const<\/span> PageTemplate = availableTemplates&#91;templateData.template?.id];\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\/\/Queries would then be available at<\/span>\n<\/span><\/span><span class='shcb-loc'><span>PageTemplate.queries\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\">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<p>This didn\u2019t work. Some console logs quickly made sense of the issue:&nbsp;<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" 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>  PageTemplate: {\n<\/span><\/span><span class='shcb-loc'><span>    '$$typeof': Symbol(react.forward_ref),\n<\/span><\/span><span class='shcb-loc'><span>    render: &#91;Function: LoadableComponent] {\n<\/span><\/span><span class='shcb-loc'><span>      preload: &#91;Function (anonymous)],\n<\/span><\/span><span class='shcb-loc'><span>      displayName: 'LoadableComponent'\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-7\"><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><\/p>\n\n\n\n<p>What\u2019s actually being loaded is the wrapper component from <code>next\/dynamic<\/code>, not the component itself. Thus, it doesn\u2019t have the <code>queries<\/code> value I added. But since this is an async component, I suspected I should be able to access <code>queries<\/code> if I load the component itself via the <code>preload<\/code> function.<\/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><span class=\"hljs-keyword\">const<\/span> component = <span class=\"hljs-keyword\">await<\/span> PageTemplate.render.preload();\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><\/p>\n\n\n\n<p>Sure enough, this worked:<\/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><span class=\"hljs-keyword\">const<\/span> component = <span class=\"hljs-keyword\">await<\/span> PageTemplate.render.preload();\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-comment\">\/\/ Queries available at:<\/span>\n<\/span><\/span><span class='shcb-loc'><span>component.default.queries\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<p>Now that we have loaded our module and have access to queries, our array of queries will be handed off to a purpose-built function that can handle executing all the queries with their given config and variables, returning them in the expected structure. All together this will look something like:<\/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><span class=\"hljs-comment\">\/\/ src\/pages\/&#91;&#91;...uri]].js<\/span>\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">import<\/span> { uriToTemplate } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"@\/lib\/templateHierarchy\"<\/span>;\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">import<\/span> availableTemplates <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"@\/wp-templates\"<\/span>;\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">import<\/span> { fetchQueries } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"@\/lib\/queryHandler\"<\/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\">async<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">getServerSideProps<\/span>(<span class=\"hljs-params\">context<\/span>) <\/span>{\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">const<\/span> { params } = context;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">const<\/span> uri = <span class=\"hljs-built_in\">Array<\/span>.isArray(params.uri)\n<\/span><\/span><span class='shcb-loc'><span>    ? <span class=\"hljs-string\">\"\/\"<\/span> + params.uri?.join(<span class=\"hljs-string\">\"\/\"<\/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> templateData = <span class=\"hljs-keyword\">await<\/span> uriToTemplate({ uri });\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">if<\/span> (\n<\/span><\/span><span class='shcb-loc'><span>    !templateData?.template?.id ||\n<\/span><\/span><span class='shcb-loc'><span>    templateData?.template?.id === <span class=\"hljs-string\">\"404 Not Found\"<\/span>\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=\"hljs-attr\">notFound<\/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>  <span class=\"hljs-keyword\">const<\/span> PageTemplate = availableTemplates&#91;templateData.template?.id];\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">const<\/span> component = <span class=\"hljs-keyword\">await<\/span> PageTemplate.render.preload();\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">const<\/span> graphqlData = <span class=\"hljs-keyword\">await<\/span> fetchQueries({\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-attr\">queries<\/span>: component.default.queries,\n<\/span><\/span><span class='shcb-loc'><span>    context,\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-attr\">props<\/span>: {\n<\/span><\/span><span class='shcb-loc'><span>      uri,\n<\/span><\/span><span class='shcb-loc'><span>      templateData,\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\">return<\/span> {\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-attr\">props<\/span>: {\n<\/span><\/span><span class='shcb-loc'><span>      uri,\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-comment\">\/\/ https:\/\/github.com\/vercel\/next.js\/discussions\/11209#discussioncomment-35915<\/span>\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">templateData<\/span>: <span class=\"hljs-built_in\">JSON<\/span>.parse(<span class=\"hljs-built_in\">JSON<\/span>.stringify(templateData)),\n<\/span><\/span><span class='shcb-loc'><span>      <span class=\"hljs-attr\">graphqlData<\/span>: <span class=\"hljs-built_in\">JSON<\/span>.parse(<span class=\"hljs-built_in\">JSON<\/span>.stringify(graphqlData)),\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-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<h3 class=\"wp-block-heading\">Component Queries<\/h3>\n\n\n\n<p>I like to be able co-locate my queries with the components they go with. So, leveraging the existing queries system I can similarly export query from individual components. For a navigation menu I could opt to pass the desired menu location in from the template to determine which menu is fetched and rendered.<\/p>\n\n\n\n<p>In this example, I kept it simple and rendered a \u201cRecent Posts\u201d component on the home page.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-11\" 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-keyword\">import<\/span> { gql } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"urql\"<\/span>;\n<\/span><\/span><span class='shcb-loc'><span><span class=\"hljs-keyword\">import<\/span> { useRouteData } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"@\/lib\/context\"<\/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-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">RecentPosts<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">const<\/span> { graphqlData } = useRouteData();\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">const<\/span> posts = graphqlData?.RecentPosts?.data?.posts?.nodes || &#91;];\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">if<\/span> (graphqlData?.RecentPosts?.error) {\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-built_in\">console<\/span>.error(<span class=\"hljs-string\">\"Error fetching RecentPosts:\"<\/span>, graphqlData.RecentPosts.error);\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"hljs-keyword\">return<\/span> <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>Error loading recent posts.<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/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\">return<\/span> (\n<\/span><\/span><span class='shcb-loc'><span>    <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"recent-posts\"<\/span>&gt;<\/span><\/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\">h2<\/span>&gt;<\/span>Recent Posts<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h2<\/span>&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\">      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">ul<\/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\">        {posts.map((post) =&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\">li<\/span> <span class=\"hljs-attr\">key<\/span>=<span class=\"hljs-string\">{post.id}<\/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\">a<\/span> <span class=\"hljs-attr\">href<\/span>=<span class=\"hljs-string\">{post.uri}<\/span>&gt;<\/span>{post.title}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">a<\/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\">li<\/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><\/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\">&lt;\/<span class=\"hljs-name\">ul<\/span>&gt;<\/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><\/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\"><\/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\">RecentPosts.query = {<\/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\">query<\/span>: gql<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-string\">    query RecentPosts {<\/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-string\">      posts(first: 5) {<\/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-string\">        nodes {<\/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-string\">          id<\/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-string\">          title<\/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-string\">          uri<\/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-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-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-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-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><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span>\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><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<p>You may have noticed I used custom context to fetch the data. While I could pass this via props fairly easily, that\u2019s not always the case. To avoid prop drilling, I added a context provider to our catch-all route to make page props available to all components.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-12\" 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-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">Page<\/span>(<span class=\"hljs-params\">props<\/span>) <\/span>{\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">const<\/span> { templateData } = props;\n<\/span><\/span><span class='shcb-loc'><span>\n<\/span><\/span><span class='shcb-loc'><span>  <span class=\"hljs-keyword\">const<\/span> PageTemplate = availableTemplates&#91;templateData.template?.id];\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\">RouteDataProvider<\/span> <span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">{props}<\/span>&gt;<\/span><\/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\">PageTemplate<\/span> {<span class=\"hljs-attr\">...props<\/span>} \/&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\">RouteDataProvider<\/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><\/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><\/span><\/span><\/span>\n<\/span><\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><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<h2 class=\"wp-block-heading\">Wrapping up<\/h2>\n\n\n\n<p>Just like that, we\u2019ve managed to implement a template-hierarchy router and GraphQL data fetching for our templates. All the while, we have avoided some performance issues by enabling dynamic imports for templates and multiple query support for data fetching.\u00a0<\/p>\n\n\n\n<p>This implementation is far from production-ready. I can think of a number of things the GraphQL data fetching doesn\u2019t handle yet. But this shows us that with a little problem-solving, we can build some great solutions.<\/p>\n\n\n\n<p>That said, between Astro, SvelteKit, and Next.js. Next has proven to be the most complicated implementation. The non-standard <code>next\/dynamic<\/code> means extra steps for queries and manual registration of our <code>wp-templates<\/code>.<\/p>\n\n\n\n<p>This comes down to strong async support in Astro and SvelteKit, while React has long struggled with supporting async data. Admittedly, Next App Router would likely help us simplify implementation complexities. But that\u2019s a story for another day.<\/p>\n\n\n\n<p>While my relationship with React\/Next is tenuous at best, and I strongly prefer anything but, I still make a living maintaining sites using these technologies, and I learned a bunch about using them with headless WordPress. What do you think?<br><br><br><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Next.js is one of the most popular front-ends for building with headless WordPress. My Reddit notifications are littered with Next.js + headless WordPress recommendations. Today, we\u2019re going to look at [&hellip;]<\/p>\n","protected":false},"author":25,"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":[25,26],"class_list":["post-31946","post","type-post","status-publish","format-standard","hentry","category-headless","tag-faust-js","tag-wpgraphql"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.7 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Next.js + WordPress: Routing and GraphQL - Builders<\/title>\n<meta name=\"description\" content=\"Learn Next.js + headless WordPress for enhanced routing and GraphQL! \ud83d\ude80 This deep dive explores fixing common issues like bundle splitting &amp; query optimization, offering alternative data fetching methods. Learn to build a template-hierarchy router and optimize GraphQL data fetching.\" \/>\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\/next-js-wordpress-routing-and-graphql\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Next.js + WordPress: Routing and GraphQL\" \/>\n<meta property=\"og:description\" content=\"Master Next.js + headless WordPress for enhanced routing and GraphQL! \ud83d\ude80 This deep dive explores fixing common issues like bundle splitting &amp; query optimization, offering alternative data fetching methods. Learn to build a template-hierarchy router and optimize GraphQL data fetching.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/wpengine.com\/builders\/next-js-wordpress-routing-and-graphql\/\" \/>\n<meta property=\"og:site_name\" content=\"Builders\" \/>\n<meta property=\"article:published_time\" content=\"2025-08-04T22:33:27+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-08-07T20:37:45+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2025\/08\/WPE-Builders-Contributing-to-\u2028Open-Source-Projects-1920x1080-1-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=\"Alex Moon\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@moon_meister\" \/>\n<meta name=\"twitter:site\" content=\"@wpebuilders\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Alex Moon\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"7 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/wpengine.com\/builders\/next-js-wordpress-routing-and-graphql\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/wpengine.com\/builders\/next-js-wordpress-routing-and-graphql\/\"},\"author\":{\"name\":\"Alex Moon\",\"@id\":\"https:\/\/wpengine.com\/builders\/#\/schema\/person\/868e9d9f8b846000f45cc7a25f46255a\"},\"headline\":\"Next.js + WordPress: Routing and GraphQL\",\"datePublished\":\"2025-08-04T22:33:27+00:00\",\"dateModified\":\"2025-08-07T20:37:45+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/wpengine.com\/builders\/next-js-wordpress-routing-and-graphql\/\"},\"wordCount\":1585,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/wpengine.com\/builders\/#organization\"},\"keywords\":[\"Faust.js\",\"WPGraphQL\"],\"articleSection\":[\"Headless\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/wpengine.com\/builders\/next-js-wordpress-routing-and-graphql\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/wpengine.com\/builders\/next-js-wordpress-routing-and-graphql\/\",\"url\":\"https:\/\/wpengine.com\/builders\/next-js-wordpress-routing-and-graphql\/\",\"name\":\"Next.js + WordPress: Routing and GraphQL - Builders\",\"isPartOf\":{\"@id\":\"https:\/\/wpengine.com\/builders\/#website\"},\"datePublished\":\"2025-08-04T22:33:27+00:00\",\"dateModified\":\"2025-08-07T20:37:45+00:00\",\"description\":\"Learn Next.js + headless WordPress for enhanced routing and GraphQL! \ud83d\ude80 This deep dive explores fixing common issues like bundle splitting & query optimization, offering alternative data fetching methods. Learn to build a template-hierarchy router and optimize GraphQL data fetching.\",\"breadcrumb\":{\"@id\":\"https:\/\/wpengine.com\/builders\/next-js-wordpress-routing-and-graphql\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/wpengine.com\/builders\/next-js-wordpress-routing-and-graphql\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/wpengine.com\/builders\/next-js-wordpress-routing-and-graphql\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/wpengine.com\/builders\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Next.js + WordPress: Routing and GraphQL\"}]},{\"@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\/868e9d9f8b846000f45cc7a25f46255a\",\"name\":\"Alex Moon\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/wpengine.com\/builders\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/999d8bf58fa6790945f2aff83a31c3df?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/999d8bf58fa6790945f2aff83a31c3df?s=96&d=mm&r=g\",\"caption\":\"Alex Moon\"},\"description\":\"Alex Moon is a Developer Advocate at WP Engine. He's been working in Open Source for over a decade and headless WordPress since the early days of WP GraphQL. When he's not writing code he's often found in the mountains or sailing the seas. Follow him on X or Bluesky for more headless WordPress goodness!\",\"sameAs\":[\"https:\/\/gurugoes.net\",\"https:\/\/x.com\/moon_meister\"],\"url\":\"https:\/\/wpengine.com\/builders\/author\/alexmoon-2-2-2-2-2-2-2-2-2-2-2\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Next.js + WordPress: Routing and GraphQL - Builders","description":"Learn Next.js + headless WordPress for enhanced routing and GraphQL! \ud83d\ude80 This deep dive explores fixing common issues like bundle splitting & query optimization, offering alternative data fetching methods. Learn to build a template-hierarchy router and optimize GraphQL data fetching.","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\/next-js-wordpress-routing-and-graphql\/","og_locale":"en_US","og_type":"article","og_title":"Next.js + WordPress: Routing and GraphQL","og_description":"Master Next.js + headless WordPress for enhanced routing and GraphQL! \ud83d\ude80 This deep dive explores fixing common issues like bundle splitting & query optimization, offering alternative data fetching methods. Learn to build a template-hierarchy router and optimize GraphQL data fetching.","og_url":"https:\/\/wpengine.com\/builders\/next-js-wordpress-routing-and-graphql\/","og_site_name":"Builders","article_published_time":"2025-08-04T22:33:27+00:00","article_modified_time":"2025-08-07T20:37:45+00:00","og_image":[{"width":1024,"height":576,"url":"https:\/\/wpengine.com\/builders\/wp-content\/uploads\/2025\/08\/WPE-Builders-Contributing-to-\u2028Open-Source-Projects-1920x1080-1-1024x576.png","type":"image\/png"}],"author":"Alex Moon","twitter_card":"summary_large_image","twitter_creator":"@moon_meister","twitter_site":"@wpebuilders","twitter_misc":{"Written by":"Alex Moon","Est. reading time":"7 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/wpengine.com\/builders\/next-js-wordpress-routing-and-graphql\/#article","isPartOf":{"@id":"https:\/\/wpengine.com\/builders\/next-js-wordpress-routing-and-graphql\/"},"author":{"name":"Alex Moon","@id":"https:\/\/wpengine.com\/builders\/#\/schema\/person\/868e9d9f8b846000f45cc7a25f46255a"},"headline":"Next.js + WordPress: Routing and GraphQL","datePublished":"2025-08-04T22:33:27+00:00","dateModified":"2025-08-07T20:37:45+00:00","mainEntityOfPage":{"@id":"https:\/\/wpengine.com\/builders\/next-js-wordpress-routing-and-graphql\/"},"wordCount":1585,"commentCount":0,"publisher":{"@id":"https:\/\/wpengine.com\/builders\/#organization"},"keywords":["Faust.js","WPGraphQL"],"articleSection":["Headless"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/wpengine.com\/builders\/next-js-wordpress-routing-and-graphql\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/wpengine.com\/builders\/next-js-wordpress-routing-and-graphql\/","url":"https:\/\/wpengine.com\/builders\/next-js-wordpress-routing-and-graphql\/","name":"Next.js + WordPress: Routing and GraphQL - Builders","isPartOf":{"@id":"https:\/\/wpengine.com\/builders\/#website"},"datePublished":"2025-08-04T22:33:27+00:00","dateModified":"2025-08-07T20:37:45+00:00","description":"Learn Next.js + headless WordPress for enhanced routing and GraphQL! \ud83d\ude80 This deep dive explores fixing common issues like bundle splitting & query optimization, offering alternative data fetching methods. Learn to build a template-hierarchy router and optimize GraphQL data fetching.","breadcrumb":{"@id":"https:\/\/wpengine.com\/builders\/next-js-wordpress-routing-and-graphql\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/wpengine.com\/builders\/next-js-wordpress-routing-and-graphql\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/wpengine.com\/builders\/next-js-wordpress-routing-and-graphql\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/wpengine.com\/builders\/"},{"@type":"ListItem","position":2,"name":"Next.js + WordPress: Routing and GraphQL"}]},{"@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\/868e9d9f8b846000f45cc7a25f46255a","name":"Alex Moon","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/wpengine.com\/builders\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/999d8bf58fa6790945f2aff83a31c3df?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/999d8bf58fa6790945f2aff83a31c3df?s=96&d=mm&r=g","caption":"Alex Moon"},"description":"Alex Moon is a Developer Advocate at WP Engine. He's been working in Open Source for over a decade and headless WordPress since the early days of WP GraphQL. When he's not writing code he's often found in the mountains or sailing the seas. Follow him on X or Bluesky for more headless WordPress goodness!","sameAs":["https:\/\/gurugoes.net","https:\/\/x.com\/moon_meister"],"url":"https:\/\/wpengine.com\/builders\/author\/alexmoon-2-2-2-2-2-2-2-2-2-2-2\/"}]}},"_links":{"self":[{"href":"https:\/\/wpengine.com\/builders\/wp-json\/wp\/v2\/posts\/31946","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\/25"}],"replies":[{"embeddable":true,"href":"https:\/\/wpengine.com\/builders\/wp-json\/wp\/v2\/comments?post=31946"}],"version-history":[{"count":0,"href":"https:\/\/wpengine.com\/builders\/wp-json\/wp\/v2\/posts\/31946\/revisions"}],"wp:attachment":[{"href":"https:\/\/wpengine.com\/builders\/wp-json\/wp\/v2\/media?parent=31946"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/wpengine.com\/builders\/wp-json\/wp\/v2\/categories?post=31946"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/wpengine.com\/builders\/wp-json\/wp\/v2\/tags?post=31946"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}