A modern headless WordPress starter built with Next.js 16, React 19, and TypeScript.
- Quick Start
- Prerequisites
- Environment Variables
- Features
- Project Structure
- Deployment
- WordPress API Functions
- Cache Revalidation
- Customization
- Troubleshooting
- Scripts
- Contributing
- License
- Credits
# Clone the repository git clone https://github.com/9d8dev/next-wp.git cd next-wp # Install dependencies pnpm install # Set up environment variables cp .env.example .env.local # Edit .env.local with your WordPress URL and credentials# Start development server pnpm devYour site is now running at http://localhost:3000.
- Node.js 18.17 or later
- pnpm 8.0 or later (recommended) or npm/yarn
- WordPress site with REST API enabled (default in WordPress 4.7+)
Create a .env.local file in the root directory:
WORDPRESS_URL="https://your-wordpress-site.com"# Full WordPress URL WORDPRESS_HOSTNAME="your-wordpress-site.com"# Domain for image optimization WORDPRESS_WEBHOOK_SECRET="your-secret-key-here"# Secret for cache revalidation- Type-safe WordPress API - Full TypeScript support with comprehensive type definitions
- Server-side pagination - Efficient handling of large content libraries
- Automatic cache revalidation - WordPress plugin for instant updates
- Dynamic routes - Posts, pages, authors, categories, and tags
- Search & filtering - Real-time search with debouncing
- Dynamic sitemap - Auto-generated XML sitemap
- OG image generation - Dynamic social media cards
- Dark mode - Built-in theme switching
- shadcn/ui components - Beautiful, accessible UI components
- Responsive design - Mobile-first with Tailwind CSS v4
next-wp/ ├── app/ # Next.js App Router │ ├── api/ │ │ ├── og/ # OG image generation │ │ └── revalidate/ # Cache revalidation webhook │ ├── pages/[slug]/ # Dynamic WordPress pages │ ├── posts/ │ │ ├── [slug]/ # Individual post pages │ │ ├── authors/ # Author archive │ │ ├── categories/ # Category archive │ │ └── tags/ # Tag archive │ ├── layout.tsx # Root layout │ ├── page.tsx # Homepage │ └── sitemap.ts # Dynamic sitemap ├── components/ │ ├── posts/ # Post-related components │ │ ├── post-card.tsx # Post card component │ │ ├── filter.tsx # Filter controls │ │ └── search-input.tsx # Search component │ ├── nav/ # Navigation components │ ├── theme/ # Theme toggle │ └── ui/ # shadcn/ui components ├── lib/ │ ├── wordpress.ts # WordPress API functions │ └── wordpress.d.ts # TypeScript definitions ├── plugin/ # WordPress revalidation plugin ├── menu.config.ts # Navigation configuration ├── site.config.ts # Site metadata └── CLAUDE.md # AI assistant guidelines Railway deploys the complete stack with one click: MySQL + WordPress + Next.js.
The Railway template uses a custom WordPress Docker image (ghcr.io/9d8dev/next-wp-wordpress) with:
- next-revalidate plugin - Pre-installed and auto-activated for cache revalidation
- nextjs-headless theme - Redirects WordPress frontend to your Next.js site
- WP-CLI - Automated WordPress setup
- MySQL 8.0 - Database with persistent volume
- Next.js - Your frontend application
┌─────────┐ ┌───────────┐ ┌─────────┐ │ MySQL │────▶│ WordPress │◀────│ Next.js │ │ DB │ │ (CMS) │ │(Frontend)│ └─────────┘ └───────────┘ └─────────┘ - Click the Deploy on Railway button above
- Wait for all 3 services to deploy (MySQL, WordPress, Next.js)
- Note the WordPress and Next.js public URLs from the Railway dashboard
1. Complete WordPress Installation
- Visit your WordPress URL (e.g.,
https://wordpress-xxx.up.railway.app) - Complete the installation wizard:
- Site Title
- Admin Username
- Admin Password
- Admin Email
- Click "Install WordPress"
2. Configure the Revalidation Plugin
The next-revalidate plugin is pre-installed and activated.
- Go to WordPress Admin → Settings → Next.js Revalidation
- Enter your Next.js URL (e.g.,
https://next-wp-xxx.up.railway.app) - Enter the Webhook Secret:
- In Railway, go to your Next.js service → Variables
- Copy the
WORDPRESS_WEBHOOK_SECRETvalue - Paste it in the plugin settings
- Click Save
3. Test the Setup
- Create a test post in WordPress and publish it
- Visit your Next.js site - the post should appear
- Edit the post in WordPress
- Refresh the Next.js site - changes should appear (revalidation working)
By default, the template deploys from the 9d8dev/next-wp repository. To customize:
- In Railway, click on the Next.js service
- Go to Settings → Source → Upstream Repo
- Click "Eject"
- Select your GitHub account/organization
- Click "Eject service"
Railway creates a copy of the repository in your GitHub. You can then:
- Clone the repo locally
- Make customizations (styling, components, pages)
- Push changes → Railway auto-deploys
- Click the Deploy with Vercel button above
- Fill in environment variables:
WORDPRESS_URL- Your existing WordPress site URLWORDPRESS_HOSTNAME- WordPress domain (for images)WORDPRESS_WEBHOOK_SECRET- Generate a secure random string
- Deploy and wait for build to complete
- Install the revalidation plugin on your WordPress site
- Configure the plugin with your Vercel deployment URL
# Install dependencies pnpm install # Copy environment template cp .env.example .env.local # Configure your WordPress connection in .env.local# Then start the dev server pnpm devRequired: Your WordPress site must have the REST API enabled (default since WP 4.7).
All WordPress interactions are centralized in lib/wordpress.ts:
getAllPosts(filters?)// Get all posts (max 100)getPostsPaginated(page,perPage,filters?)// Paginated postsgetPostBySlug(slug)// Single post by sluggetPostById(id)// Single post by IDgetAllCategories()// All categoriesgetCategoryBySlug(slug)// Category by sluggetAllTags()// All tagsgetTagBySlug(slug)// Tag by sluggetPostsByCategory(id)// Posts in categorygetPostsByTag(id)// Posts with taggetAllAuthors()// All authorsgetAuthorBySlug(slug)// Author by sluggetPostsByAuthor(id)// Posts by authorgetAllPages()// All pagesgetPageBySlug(slug)// Page by slugimport{getPostsPaginated}from"@/lib/wordpress";const{data: posts, headers }=awaitgetPostsPaginated(1,9,{category: "news",search: "nextjs"});console.log(`Found ${headers.total} posts across ${headers.totalPages} pages`);The starter uses Next.js cache tags for efficient revalidation:
- Install the plugin - Download next-revalidate.zip and upload to WordPress
- Configure - Go to Settings > Next.js Revalidation
- Set URL - Enter your Next.js site URL
- Set secret - Use the same
WORDPRESS_WEBHOOK_SECRETvalue
When content changes in WordPress, only affected pages are revalidated.
Note: If using the Railway template, the plugin is pre-installed automatically.
Edit site.config.ts for site metadata:
exportconstsiteConfig={site_name: "Your Site",site_domain: "yourdomain.com",site_description: "Your site description"};Edit menu.config.ts for navigation links:
exportconstmainMenu=[{href: "/",label: "Home"},{href: "/posts",label: "Blog"},// Add more links...];This project uses shadcn/ui with Tailwind CSS. Customize colors in your CSS or update the shadcn theme.
- Ensure your WordPress site is publicly accessible
- Check that permalinks are set (Settings > Permalinks)
- Verify REST API at
your-site.com/wp-json/wp/v2/posts
- Add your WordPress domain to
WORDPRESS_HOSTNAME - Check
next.config.tshas the correctremotePatterns
- Verify
WORDPRESS_WEBHOOK_SECRETmatches in both WordPress and Next.js - Check the plugin is activated in WordPress
- Test the webhook endpoint at
/api/revalidate
- Install a CORS plugin on WordPress, or
- Configure your server to allow requests from your Next.js domain
pnpm dev # Start development server pnpm build # Build for production pnpm start # Start production server pnpm lint # Run ESLintContributions are welcome! Please:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
MIT License - see LICENSE for details.
Built with Next.js, Tailwind CSS, shadcn/ui, and brijr/craft.
Created by Bridger Tower and Cameron Youngblood at 9d8.


