Building a Decoupled WordPress Front End With Gatsby
Gatsby, the React-based static site framework, has attracted plenty of WordPress users who want faster image handling and better security posture without surrendering the WordPress admin and editor as their content management layer. Combining the two is possible by exposing WordPress data through GraphQL and sourcing it into Gatsby at build time.
What follows is a practical walkthrough of that setup, based on a project that ported a WordPress site to Gatsby. Note that the toolchain involved — WPGraphQL and the Gatsby CLI — is under active development and recent versions have introduced breaking changes. This project used WPGraphQL 0.8.3, gatsby-source-wpgraphql 2.5.1, and gatsby-cli 2.12.21. Unlike WordPress, newer WPGraphQL releases do not guarantee backward compatibility, so check the official documentation for the latest versions before starting.
Existing starters in the Gatsby starters library can shortcut the process. Two solid references are Alexandra Spalato’s gatsby-wordpress-theme-blog and Zac Gordon and Muhammad Muhsin’s twenty-nineteen-gatsby-theme.
Prerequisites and Resources
Following along requires basic familiarity with React and JavaScript, a working understanding of Gatsby and its dynamic page creation model, plus a WordPress installation you can use as a data source.
Several existing guides helped shape the process below: Henrik Wirth’s overview of the Gatsby WordPress starter, Jason Lengstorf’s migration walkthrough on the Netlify blog, and Muhammad Muhsin’s write-up on porting the Twenty Nineteen theme. Wirth’s guide is particularly thorough, covering image handling and ACF PageBuilder work that aren’t included here. Muhsin’s article breaks down how static pages are generated with Gatsby’s createPages API and explains the template files and React components involved.
Setting Up WordPress and Gatsby
Start with a WordPress site — existing, fresh, or even a local install — running the default Twenty Twenty theme. Two plugins need to be installed from their GitHub repositories rather than the WordPress Plugin Directory, since neither is available there at the time of writing. Download the ZIP files and install them manually into /wp-content/plugins.
WPGraphQL enables a GraphQL API in WordPress, turning the site into a data source. WPGraphiQL (note the “i”) is optional but provides a GraphQL playground directly in the WordPress dashboard for testing queries.
On the Gatsby side, scaffold a local site with the default starter in a wordpress-gatsby directory, then restart the development server so the starter page appears at localhost:8000.
Next, install and configure the gatsby-source-graphql plugin. In gatsby-config.js, point the plugin at the WordPress GraphQL endpoint and set two configuration options: typeName, which defines the remote schema query type, and fieldName, which becomes available in the Gatsby query. The current WPGraphQL docs recommend using fieldName: "wpcontent" rather than "wpgraphql" as earlier guides described.
An alternative to hard-coding the WordPress URL is the dotenv npm module, which lets you define environment variables such as WORDPRESS_URL in a .env.production file. This keeps the WordPress instance and its exposed data out of the Gatsby configuration.
Once the development server restarts, the WPGraphQL API is queryable from Gatsby through the localhost GraphQL endpoint at localhost:8000/___graphql/.
Porting Posts and Pages
Gatsby creates posts and pages at build time by querying data with GraphQL and mapping results to templates. The official tutorial explains the two relevant APIs: onCreateNode and createPages.
WordPress stores content under varied data types and categories, which makes porting everything less than straightforward. Prior knowledge of Gatsby’s createPages API and Node APIs helps considerably. Starter sites provide additional real-world reference.
Setting Up Content and Templates
Before creating pages from WordPress data, delete index.js and page-2.js from the Gatsby site’s pages folder — these interfere with the ported content.
Create two template files: one for posts under /src/templates/posts/index.js, and one for pages under /src/templates/pages/index.js. The post template uses the post title twice — once for the SEO title and once as the visible heading — and renders the post content through a dedicated component. The page template works nearly identically.
Generating Static Pages
The orchestration code can live entirely in gatsby-node.js, but separating posts and pages into a folder named create at the project root keeps things readable. Add the appropriate calls to gatsby-node.js first.
The createPages API is part of the Node APIs Gatsby exposes; it instructs Gatsby to add pages, using async/await for the asynchronous calls. Define which data to fetch and retrieve it in create/createPages.js, with comments explaining each step.
Creating Posts
The createPosts.js file is nearly identical to createPages.js, differing only in that it prefixes paths with blog/ and swaps “page” terminology for “post.”
If the development server is restarted at this stage, the log will show the page buildup — but opening localhost:8000 returns a 404 error. This is expected. Clicking links on the 404 page displays the correct WordPress-sourced content; the sample-page link, for instance, renders the corresponding page content from WordPress.
Working With Navigation
WordPress’s menu management supports links to pages, posts, archives, taxonomies, and custom URLs. The goal is to create a main menu in WordPress, send it to GraphQL, and query it onto the Gatsby site.
Internal navigation in Gatsby uses the built-in <Link> component and its companion navigate function from the Gatsby Link API. External links need a regular anchor tag instead. Porting the WordPress menu therefore requires custom <Menu> and <MenuItem> components and a refactor of the existing <Layout> component.
These patterns appear in several Gatsby WordPress starters with little variation from the snippets below.
Creating the WordPress Menu
Set up a menu called “PRIMARY,” the name expected by the Twenty Twenty theme. Add at least three items: a custom Home link to the site index, a link to the default Sample Page WordPress creates, and a Front Page (created in the WordPress editor).
Querying Menu Items
With the GraphiQL interface, checking a few boxes in the explorer constructs the query for menu items automatically.
URL Handling in Gatsby
WordPress returns absolute URLs; convert these to relative paths for Gatsby’s <Link> component with a small utility function.
Building the Menu Components
The <MenuItem> component consumes that utility and produces a fully formed link. Those items live inside a new <Menu> component, which uses Gatsby’s StaticQuery API to fetch all primary menu items. Drop <Menu> into <Layout>, and the site has a navigation menu driven by WordPress data.
Gatsby’s documentation on the <Link> component notes that data from an external CMS should ideally be inspected — rendering with Gatsby’s <Link> for internal links or a regular <a> tag for genuine external URLs. That requires a <UniversalLink> component that returns the appropriate element based on the URL. Update <MenuItem> to use it, restart with gatsby develop, and the browser should show a navigation menu with relative paths.
Displaying Blog Posts
Displaying posts in Gatsby involves blog templates plus components for post entries, featured images, and pagination, all tied together in the existing createPages.js and createPosts.js files.
Global Variables
A globals file at the project root defines the blogURI path. With the WordPress reading setting at “Your latest posts,” blogURI = ''. If using the “static page” option for the homepage, set blogURI = 'blog' instead.
Templates and Components
The blog template handles the full list of published posts using two components. PostEntry iterates through posts, rendering each post entry title, featured image (when present), excerpt, and slug. An optional image component fetches the WordPress featured image file and assigns a fallback when none exists; setting withFallback to false means no DOM element renders.
The Pagination component controls the number of posts per page, using the Next/Previous link variant of WordPress pagination. A conditional returns null when pageNumber === 1 && !hasNextPage; the Previous button appears when hasPageNumber exceeds 1, and Next renders when hasNextPage is at least 1.
Refactoring With GraphQL Fragments
The createPages.js file grows unwieldy with everything it tracks. GraphQL fragments — reusable field sets that can be included in queries wherever needed — keep things organized. Store the fragment definitions for the post template and post preview in a data.js file and import them at the top of createPosts.js. Register those fragment strings outside the GET_POSTS query and reference them inside it. At the bottom of createPosts.js, define the blogPage path using the global blogURI variable and add code to produce paginated blog pages.
With the local server restarted, the browser displays a loop of published posts containing title and excerpt.
Styling and Deployment
Gatsby’s documentation covers global CSS files, modular stylesheets, CSS-in-JS, and additional options including Typography.js, Sass, JSS, Stylus, and PostCSS. Porting a full WordPress theme takes extra work: Muhsin’s Twenty Nineteen port adapted vw CSS units so they worked with flexbox, for example, while Wirth’s Twenty Twenty starter ported both the stylesheet and its fonts.
Using Sass in this project required installing gatsby-plugin-sass and its node-sass dependency, then adding the plugin to gatsby-config.js. Styles in .scss files import normally, either into the global <Layout> component or gatsby-browser.js with a require statement.
WordPress block editor content also has its own set of styles that need porting. Jason Lengstorf’s guide demonstrates installing the WordPress blocks package and importing its styles into a Gatsby component. Since the block editor is still actively developed, expect potential breaking changes.
For hosting, Netlify hooks into the project’s GitHub repository and deploys automatically when changes are pushed to the production branch. The JAMstack Deployments plugin extends that continuous deployment workflow to WordPress itself, triggering rebuilds whenever posts are published or pages edited.
This decoupled setup is not complete. WordPress stores authors, categories, tags, post statuses, custom post types, and more, all of which take additional consideration before they can be represented in Gatsby. Useful reference implementations are growing, though: Wirth maintains an awesome list of WordPress-Gatsby resources, and both the Twenty Nineteen and Twenty Twenty theme ports have live demo sites.



