Skip to content

Commit ccb78bd

Browse files
authored
Merge pull request #103 from wpengine/example-next-pages-file-based-routing
feat: Add next.js pages router file based routing example
2 parents 5139401 + 0360644 commit ccb78bd

File tree

30 files changed

+6349
-2
lines changed

30 files changed

+6349
-2
lines changed

README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,9 @@
33
Headless WordPress Toolkit
44

55
> A modern toolkit for building headless WordPress applications. Provides CLI tools, plugins, and best practices for decoupling WordPress into a powerful headless CMS.
6-
76
## Project Organization
87

98
- `packages`: Dependency packages to be published on NPM
109
- `plugins`: PHP based WordPress Plugins
1110
- `docs`: Documentation for the Toolkit?
12-
- `examples`: Example code for HWP
11+
- `examples`: Example code for HWP
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2+
3+
# dependencies
4+
/node_modules
5+
/.pnp
6+
.pnp.*
7+
.yarn/*
8+
!.yarn/patches
9+
!.yarn/plugins
10+
!.yarn/releases
11+
!.yarn/versions
12+
13+
# testing
14+
/coverage
15+
16+
# next.js
17+
/.next/
18+
/out/
19+
20+
# production
21+
/build
22+
23+
# misc
24+
.DS_Store
25+
*.pem
26+
27+
# debug
28+
npm-debug.log*
29+
yarn-debug.log*
30+
yarn-error.log*
31+
.pnpm-debug.log*
32+
33+
# env files (can opt-in for committing if needed)
34+
.env*
35+
36+
# vercel
37+
.vercel
38+
39+
# typescript
40+
*.tsbuildinfo
41+
next-env.d.ts
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
# Next.js + WPGraphQL Headless CMS
2+
3+
This is a [Next.js](https://nextjs.org) project integrated with **WPGraphQL** and **WPGraphQL for ACF** to build a headless WordPress-powered site.
4+
5+
## Prerequisites
6+
7+
Before running this project, ensure you have the following:
8+
9+
- **Node.js** (version 18 or higher recommended)
10+
- **npm**, **yarn**, **pnpm**, or **bun** package manager
11+
- A **WordPress** instance with the following plugins installed and configured:
12+
- **WPGraphQL** ([wpgraphql.com](https://www.wpgraphql.com/))
13+
- **WPGraphQL for ACF** ([acf.wpgraphql.com](https://acf.wpgraphql.com/))
14+
15+
## WordPress Setup
16+
17+
### Required Plugins
18+
1. **Install WPGraphQL**: This exposes your WordPress data via a GraphQL API.
19+
2. **Install WPGraphQL for ACF**: This allows custom fields from ACF to be queried via GraphQL.
20+
21+
### Permalink Structure
22+
This project follows a **custom permalink structure** to match Next.js file-based routing:
23+
24+
```
25+
/posts/%postname%/
26+
```
27+
28+
To set this, go to **Settings > Permalinks** in WordPress and choose **Custom Structure**, then enter the pattern.
29+
30+
### Custom Post Types (CPT)
31+
With ACF installed you can create a new Custom Post type called **Movies**.
32+
33+
The **rewrite structure** for custom post types should be set to:
34+
```php
35+
'rewrite' => array('slug' => 'movies', 'with_front' => false),
36+
```
37+
38+
This ensures URLs for movies follow:
39+
40+
```
41+
/movies/movie-title/
42+
```
43+
And not:
44+
45+
```
46+
/posts/movies/movie-title/
47+
```
48+
As a last step go to the `GraphQL` tab in the Advanced Settings and enable the `Show in GraphQL` toggle.
49+
50+
## Categories
51+
Categories are structured as:
52+
53+
```bash
54+
/category/category-name/
55+
```
56+
57+
To achieve this category structure make sure the `Category base` in the Settings -> Permalinks page has the value of `category`.
58+
59+
The rest is handled dynamically through Next.js.
60+
61+
## Environment Variables
62+
Create a `.env.local` file in the root of your project and add:
63+
64+
```ini
65+
NEXT_PUBLIC_WORDPRESS_URL=<your_wordpress_url>
66+
```
67+
Replace <your_wordpress_url> with the actual WordPress site URL (e.g., https://your-wordpress-site.com).
68+
**Do not include a trailing slash.**
69+
70+
## Project Structure
71+
This project follows Next.js file-based routing. Based on the WordPress permalink structure, the key pages are:
72+
73+
```bash
74+
src/pages
75+
├── [slug].js # Dynamic page for general posts or pages
76+
├── _app.js # Next.js global settings
77+
├── _document.js # Document structure
78+
├── api
79+
│ └── hello.js # Example API route
80+
├── category
81+
│ ├── [category].js # Dynamic page for categories
82+
│ └── index.js # Categories index page
83+
├── index.js # Homepage (lists latest posts, movies, categories)
84+
├── movies
85+
│ ├── [slug].js # Dynamic page for individual movies
86+
│ └── index.js # Movies listing page
87+
└── posts
88+
├── [slug].js # Dynamic page for individual posts
89+
└── index.js # Posts listing page
90+
```
91+
## Getting Started
92+
1. Install Dependencies:
93+
94+
```bash
95+
npm install
96+
# or
97+
yarn install
98+
# or
99+
pnpm install
100+
# or
101+
bun install
102+
```
103+
2. Run the Development Server:
104+
105+
```bash
106+
npm run dev
107+
# or
108+
yarn dev
109+
# or
110+
pnpm dev
111+
# or
112+
bun dev
113+
```
114+
115+
3. Access the Application: Open http://localhost:3000 in your browser.
116+
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { dirname } from "path";
2+
import { fileURLToPath } from "url";
3+
import { FlatCompat } from "@eslint/eslintrc";
4+
5+
const __filename = fileURLToPath(import.meta.url);
6+
const __dirname = dirname(__filename);
7+
8+
const compat = new FlatCompat({
9+
baseDirectory: __dirname,
10+
});
11+
12+
const eslintConfig = [...compat.extends("next/core-web-vitals")];
13+
14+
export default eslintConfig;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"compilerOptions": {
3+
"paths": {
4+
"@/*": ["./src/*"]
5+
}
6+
}
7+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/** @type {import('next').NextConfig} */
2+
const nextConfig = {
3+
reactStrictMode: true,
4+
};
5+
6+
export default nextConfig;

0 commit comments

Comments
 (0)