Here are the ways to get involved with this project:
Before submitting your issue, make sure it has not been mentioned earlier. You can search through the existing issues or Github Discussions.
Found a bug you can fix? Fantastic! Patches are always welcome.
-
Clone your fork and install dependencies:
git clone git@github.com:YOUR_USERNAME/nextjs-wordpress.git cd nextjs-wordpress nvm use && npm i
-
Copy
.env.exampleto.env.localand configure your WordPress URLs -
Start the development server:
npm run dev
Note: The dev server automatically runs GraphQL Code Generator to sync WordPress types before starting.
-
The app will be available at http://localhost:3000
Available Scripts:
npm run dev- Start dev server (runs codegen + cleans.nextdirectory)npm run build- Production build with TypeScript checkingnpm run start- Start production servernpm run codegen- Generate GraphQL types from WordPress schemanpm run lint- Run ESLintnpm run format- Format code with Prettier and auto-fix ESLint issuesnpm run typecheck- Run TypeScript compiler checksnpm test- Run all tests oncenpm run test:watch- Run tests in watch modenpm run validate- Complete validation suite (format → lint → typecheck → test)
- Fork the repo and create a feature/patch branch off
main - Work locally adhering to coding standards
- Run
npm run validateto ensure all checks pass - Make sure the app builds locally with
npm run build && npm run start - Push your code, open a PR, and fill out the PR template
- After peer review, the PR will be merged back into
main - Repeat ♻️
Coding Standards:
- Follow Next.js 16 patterns (async params, Server Components)
- Always check for null/undefined data with nullish coalescing (
??) - Import WordPress types from
@/lib/generated(auto-generated via GraphQL Code Generator) - Import non-WordPress types from
@/lib/types - Use TypeScript - no
anytypes - Add proper error handling - return
[]ornullinstead of throwing - Write tests alongside code changes (test-driven development)
- Target 80%+ test coverage on new features
- See AGENTS.md for detailed patterns and best practices
GraphQL Type Generation Workflow:
- Types are auto-generated from your WordPress GraphQL schema
npm run devautomatically runsnpm run codegenbefore starting- To manually regenerate types:
npm run codegen - Generated types use
Maybe<T>- always use??for null safety - WordPress types in
lib/generated.ts(auto-generated, don't edit) - Custom types in
lib/types.d.ts(hand-written, edit as needed)
Your PR must pass automated assertions, deploy to Vercel successfully, and pass a peer review before it can be merged.
This project follows test-driven development practices. Tests are required alongside all code changes.
- Vitest - Fast unit test framework with SWC plugin
- React Testing Library - Component testing with user-centric queries
- MSW v2 - Mock Service Worker for API mocking (never mock
global.fetch) - jest-axe - Accessibility testing for WCAG compliance
# Development workflow
npm test # Run all tests once
npm run test:watch # Watch mode for active development
npm run test:ui # Interactive UI mode (debugging)
npm run test:coverage # Generate coverage report
# Complete validation
npm run validate # Runs: format → lint → typecheck → testTest files must be co-located with the code they test:
components/
Header.tsx
Header.test.tsx # ✅ Component test next to component
lib/
queries/
getAllPosts.ts
getAllPosts.test.ts # ✅ Query test next to query
Component Test Example:
import {render, screen, user} from '@/test-utils'
import {axe} from 'jest-axe'
import {SearchForm} from './SearchForm'
describe('SearchForm', () => {
it('should render search input', () => {
render(<SearchForm />)
expect(screen.getByRole('searchbox')).toBeInTheDocument()
})
it('should handle user input', async () => {
render(<SearchForm />)
const input = screen.getByRole('searchbox')
await user.type(input, 'test query')
expect(input).toHaveValue('test query')
})
it('should have no accessibility violations', async () => {
const {container} = render(<SearchForm />)
const results = await axe(container)
expect(results).toHaveNoViolations()
})
})GraphQL Query Test Example:
import {server, http, HttpResponse} from '@/test-utils'
import {getAllPosts} from './getAllPosts'
describe('getAllPosts', () => {
it('should fetch posts successfully', async () => {
server.use(
http.post(process.env.NEXT_PUBLIC_WORDPRESS_GRAPHQL_URL, () => {
return HttpResponse.json({
data: {posts: {nodes: [{title: 'Test Post'}]}}
})
})
)
const posts = await getAllPosts()
expect(posts).toHaveLength(1)
expect(posts[0].title).toBe('Test Post')
})
it('should handle empty response', async () => {
server.use(
http.post(process.env.NEXT_PUBLIC_WORDPRESS_GRAPHQL_URL, () => {
return HttpResponse.json({data: {posts: {nodes: []}}})
})
)
const posts = await getAllPosts()
expect(posts).toEqual([])
})
it('should handle API errors', async () => {
server.use(
http.post(process.env.NEXT_PUBLIC_WORDPRESS_GRAPHQL_URL, () => {
return new HttpResponse(null, {status: 500})
})
)
const posts = await getAllPosts()
expect(posts).toEqual([]) // Graceful error handling
})
})Critical: Always import from @/test-utils, never directly from libraries.
// ✅ CORRECT
import {render, screen, user, waitFor, server} from '@/test-utils'
// ❌ WRONG
import {render, screen} from '@testing-library/react'
import userEvent from '@testing-library/user-event'Key exports from @/test-utils:
user- Pre-configureduserEvent.setup()(use this for all interactions)render- Custom render with providersrenderHook- Hook testing with providersserver- MSW server instancehttp,HttpResponse- MSW utilitiesscreen,waitFor- All React Testing Library exports
Never mock global.fetch - Always use MSW v2 for HTTP interception.
import {server, http, HttpResponse} from '@/test-utils'
// Override handlers for specific test cases
it('should handle 404 error', async () => {
server.use(
http.post(process.env.NEXT_PUBLIC_WORDPRESS_GRAPHQL_URL, () => {
return new HttpResponse(null, {status: 404})
})
)
const result = await fetchData()
expect(result).toBeNull()
})- Test behavior, not implementation - Focus on what users see and do
- Use descriptive test names - Explain the expected behavior
- Test edge cases - Empty responses, errors, null values
- Check accessibility - Use
jest-axeon all components - Keep tests focused - One concept per test
- Use
it.each()- Minimize test duplication with data-driven tests - Aim for 80%+ coverage - Focus on meaningful tests, not 100%
- Target: 80%+ test coverage (not 100%)
- Focus on critical paths and user-facing features
- Some unreachable edge cases are acceptable
- Tests should add value, not just increase numbers
I've found that running vercel locally is a great way to verify Edge Functions and Middleware are working as expected.
To install the Vercel CLI, run:
npm i -g vercelStart a Vercel development server locally:
vercel devThis repo is maintained by Greg Rickaby. By contributing code you grant its use under the MIT.