WordPress to Next.js Conversion Guide

Standard Operating Procedure for FFC Site Conversions

This guide documents the complete process for converting a WordPress/Divi charity website to a Next.js static site deployed on GitHub Pages. It serves as both an SOP for FFC administrators and an educational resource for charities evaluating the FFC technology stack.

Last Updated: February 2026Reference: FreeForCharity.org Migration

1. Why FFC Migrates Off WordPress

The case for static sites over WordPress for nonprofits

Free For Charity made the strategic decision to migrate all supported charity websites from WordPress/Divi to Next.js static sites. This section explains the reasoning for charities and administrators who may be unfamiliar with the trade-offs.

WordPress Challenges

  • βœ•Hosting costs: WPMUDEV hosting runs $50-100+/month per site with cPanel infrastructure
  • βœ•Security surface: PHP runtime, database, admin panel, and plugins each present attack vectors requiring constant patching
  • βœ•Plugin dependencies: Divi theme builder, Hummingbird, Smush Pro, SmartCrawl, Beehive Analytics all require updates and licensing
  • βœ•Performance overhead: Dynamic page generation, database queries, and plugin JavaScript increase load times
  • βœ•Maintenance burden: Core updates, plugin updates, PHP version upgrades, database backups, and security monitoring require ongoing effort

Static Site Advantages

  • βœ“Free hosting: GitHub Pages is completely free with unlimited bandwidth for public repos
  • βœ“Zero attack surface: No server, no database, no admin panel, no PHP. Only static HTML/CSS/JS files
  • βœ“No dependencies: No plugins to update, no licenses to renew, no hosting provider to manage
  • βœ“Blazing performance: Pre-built HTML served from GitHub/Cloudflare edge. Lighthouse scores of 90+ are standard
  • βœ“Git-based workflow: Version control, pull requests, automated testing, and CI/CD provide professional-grade development practices

Bottom line: For a typical charity website (informational content, donation links, contact forms), WordPress is overkill. A static site delivers better performance, security, and cost savings while requiring less maintenance. The content rarely changes, and when it does, a git commit and automated deployment handles it.

2. The FFC Technology Stack

What powers every FFC-supported charity website

LayerTechnologyPurposeCost
FrameworkNext.js 16 (App Router)React-based static site generator with TypeScriptFree
StylingTailwind CSS 4Utility-first CSS framework for responsive designFree
HostingGitHub PagesStatic file hosting with custom domain + HTTPSFree
DNS + DDoSCloudflareDNS management and DDoS protection (DNS-only mode, no CDN caching)Free
CI/CDGitHub ActionsAutomated build, test, security scan, deploy, and Lighthouse auditFree
Source ControlGitHubVersion control, issue tracking, pull requestsFree
TestingJest + Playwright + Lighthouse CIUnit tests, E2E tests, accessibility, performance auditsFree
Code QualityESLint + Prettier + Husky + commitlintLinting, formatting, pre-commit hooks, conventional commitsFree

Total ongoing cost: $0/month. The entire stack is free for public repositories. Compare this to $50-100+/month for WordPress hosting. For a charity with 10 websites, that is $6,000-12,000/year saved that goes directly back to the charitable mission.

3. Prerequisites and Tools

What you need before starting a conversion

Required Software

  • 1.Node.js 20.x+ (LTS) β€” JavaScript runtime
  • 2.pnpm (preferred) or npm β€” Package manager
  • 3.Git β€” Version control
  • 4.GitHub CLI (gh) β€” Repository and issue management
  • 5.VS Code β€” Recommended editor with TypeScript support

Required Access

  • 1.WordPress admin for the source site (to install Simply Static Pro)
  • 2.GitHub org membership in FreeForCharity (to create repos)
  • 3.Cloudflare dashboard access for the charity domain
  • 4.WPMUDEV/cPanel access for the existing WordPress host

FFC Repo Naming Convention

All FFC repositories follow a strict naming pattern:

  • FFC-EX-DomainName.com β€” External charity sites (e.g., FFC-EX-AllTypeTowing.com)
  • FFC-IN-DomainName.org β€” Internal/platform FFC sites (e.g., FFC-IN-ffcadmin.org)

4. Content Audit Process

Inventory everything before you start building

Before writing any code, perform a thorough audit of the existing WordPress site. This ensures no content is lost and provides a clear scope for the conversion.

4.1 β€” Query the WordPress REST API

WordPress exposes all content via its REST API. Use these endpoints to build a complete inventory:

# Get all pages

curl "https://DOMAIN/wp-json/wp/v2/pages?per_page=100&_fields=id,title,slug,status,link,modified"

# Get all posts

curl "https://DOMAIN/wp-json/wp/v2/posts?per_page=100&_fields=id,title,slug,status,link,modified"

# Get all media

curl "https://DOMAIN/wp-json/wp/v2/media?per_page=100&_fields=id,title,source_url,mime_type,media_details"

Save results to a CSV file for tracking. If more than 100 items exist, paginate with &page=2.

4.2 β€” Cross-Reference with Sitemap

Check https://DOMAIN/sitemap.xml and compare against your REST API inventory. Sometimes pages exist in the sitemap but not the API (or vice versa).

4.3 β€” Create URL Mapping Document

Map every WordPress URL to its Next.js equivalent. Categorize each page:

  • MATCHED: WordPress slug matches an existing Next.js route
  • TO BUILD: Page exists in WordPress but not yet in Next.js
  • DROP: Page is not needed (test pages, WordPress defaults, transactional pages)
  • REDIRECT: Slug changed or page dropped; needs 301 redirect

4.4 β€” Media Asset Inventory

Catalog all images from wp-content/uploads/. Cross-reference against assets already in the Next.js repo. Identify images to migrate, duplicates to remove, and files to convert to WebP format.

4.5 β€” Content Ownership Matrix

Document where every piece of content lives in the Next.js repo: which pages use JSON data files, which have inline content in components, and what the update procedure is for each type.

Important: Freeze all WordPress content edits after the audit. Any changes made to WordPress after this point may be lost in the conversion.

5. Simply Static Export

Extracting a static HTML snapshot from WordPress

Simply Static Pro is a WordPress plugin that generates a complete static HTML snapshot of your site. FFC uses this as a reference for content extraction, not as the final deployment. The actual Next.js site is built from components, not from Simply Static output.

Step 1: Pre-Export Checklist

Disable these WordPress plugins before running Simply Static to avoid interference:

  • Hummingbird (performance/caching)
  • Smush Pro (image optimization)
  • Beehive Analytics (tracking)
  • SmartCrawl SEO (may inject dynamic scripts)

In Enhanced Crawl > Plugins to Include, remove all admin-only and token-bearing plugins. Only keep Simply Static, Simply Static Pro, and plugins with actual frontend output (e.g., Strong Testimonials). Specifically remove:

  • Tawk.to Live Chat β€” embeds API tokens that trigger GitHub push protection
  • Microsoft Clarity β€” embeds project ID tokens
  • Defender Pro β€” ships webauthn/JWT JS with secret-like patterns
  • Hustle Pro, Shipper Pro, Snapshot Pro, Broken Link Checker, Branda Pro, Divi Dash, WPMU DEV Dashboard β€” admin-only, no frontend assets

Also clear the Divi Builder cache: Divi > Theme Options > Builder > Advanced > Static CSS File Generation > Clear

Step 2: Configure Simply Static Pro

Navigate to Simply Static > Settings > Deploy and configure:

SettingValue
Deployment MethodGitHub
OrganizationFreeForCharity
RepositoryFFC-EX-DomainName.com (or FFC-IN-...)
Branchsimply-static-export
Folder(leave empty for root)

The export goes to a simply-static-export branch, never to main. Create this branch before running the export:

gh api repos/FreeForCharity/REPO-NAME/git/refs -f ref=refs/heads/simply-static-export -f sha=$(gh api repos/FreeForCharity/REPO-NAME/git/refs/heads/main --jq .object.sha)

Step 3: Run Diagnostics and Export

Run Simply Static > Diagnostics first to verify no issues. Then generate the static export. The plugin will push the static HTML to your GitHub repo branch. This typically takes 5-15 minutes depending on site size.

Step 4: GitHub Push Protection Fix (Required)

GitHub enables secret scanning push protection on all public repositories by default. WordPress HTML pages often contain embedded tokens (tawk.to widget IDs, Google Maps API keys, reCAPTCHA site keys, analytics IDs) that trigger GitHub's scanner. When Simply Static tries to commit a batch of files containing a flagged pattern, the entire batch is silently dropped. The plugin still reports β€œCommitted X of Y files” and marks the export as complete, but the HTML pages never reach the branch.

Additionally, pushing 6,000+ files via the GitHub Trees API in rapid succession triggers secondary rate limiting, which causes further batch failures.

Fix: Create an org-level branch ruleset that bypasses push protection

  1. Go to GitHub Org Settings > Rulesets > New ruleset
  2. Name: FFC Simply Static Export Bypass
  3. Enforcement: Active
  4. Target: Branch, pattern: simply-static-export
  5. Bypass actors: Add Organization Admin with β€œAlways” bypass
  6. Under Rules, enable Block force pushes (keep protection) but do NOT enable β€œRequire secret scanning results”

Alternatively, use the GitHub CLI:

gh api orgs/FreeForCharity/rulesets \
  --method POST \
  -f name="FFC Simply Static Export Bypass" \
  -f target=branch \
  -f enforcement=active \
  -f 'conditions[ref_name][include][]=refs/heads/simply-static-export' \
  -f 'rules[][type]=secret_scanning' \
  -f 'bypass_actors[][actor_type]=OrganizationAdmin' \
  -f 'bypass_actors[][actor_id]=0' \
  -f 'bypass_actors[][bypass_mode]=always'

Step 5: Enable Request Throttling

In Simply Static Pro settings, check β€œThrottle GitHub Requests” to prevent secondary rate limiting when committing large exports. This adds a small delay between API calls and dramatically reduces rate limit failures.

For sites with 5,000+ URLs, also consider excluding unnecessary directories in the URLs to Exclude field:

# Exclude admin-only and irrelevant plugin assets
/wp-admin/
/wp-content/plugins/snapshot-backups/
/wp-content/plugins/shipper/
/wp-content/plugins/wp-defender/
/wp-content/plugins/hustle/
/wp-content/plugins/broken-link-checker/
/wp-content/plugins/ultimate-branding/

Step 6: Dedicated Fine-Grained PAT

Create a dedicated fine-grained Personal Access Token for Simply Static exports. Never reuse a broad-scoped PAT. Configure the token with:

  • Repository access: Only selected repositories (the target repo)
  • Permissions: Contents (Read and write) only
  • Expiration: 90 days maximum, rotate regularly
  • Name: simply-static-REPONAME for easy identification

Never share, commit, or paste PATs in chat, issues, or documentation. If a PAT is exposed, revoke it immediately at github.com/settings/tokens.

Reference implementation: See the FFC-EX-AllTypeTowing.com repository for a completed Simply Static export example.

6. Template Setup and Customization

Starting from the FFC Single Page Template

Every new FFC site starts from the FFC_Single_Page_Template repository. This template includes the complete tech stack, CI/CD pipelines, testing infrastructure, and mandatory FFC components (footer, team section).

# 1. Create new repo from template (do NOT fork β€” clean history)

gh repo create FreeForCharity/FFC-EX-CharityName.com --public

# 2. Clone and copy template files

git clone https://github.com/FreeForCharity/FFC_Single_Page_Template.git template

git clone https://github.com/FreeForCharity/FFC-EX-CharityName.com.git site

cp -r template/* site/

# 3. Install and verify

cd site && pnpm install && pnpm build

Customization Checklist

8. Mandatory Team Section

Required for ALL FFC-supported charity websites

Every FFC site must display the organization's leadership team using the standardizedTeamMemberCard component.

Component Specifications

  • Photo: 300x300px, circular crop, ring-4 white border with shadow
  • Format: WebP (not PNG or JPEG)
  • Name: Full name displayed below photo
  • Title: Role/position in the organization
  • LinkedIn: SVG icon button linking to profile

Grid Layout (Responsive)

  • Desktop: 3 columns (first row), 2 columns (second row if 5 members)
  • Tablet: 2 columns
  • Mobile: 1 column (stacked)
  • Anchor: id="team" for direct linking

Data Structure

Team member data lives in src/data/team/ as individual JSON files:

{ "name": "Clarke Moyer", "title": "Founder / President", "imageUrl": "/Images/member1.webp", "linkedinUrl": "https://www.linkedin.com/in/clarkemoyer/" }

9. Divi-to-Tailwind Component Mapping

Converting WordPress/Divi modules to React/Tailwind components

Divi uses "modules" for layout. Each module maps to a Next.js/Tailwind component:

Divi ModuleNext.js/Tailwind EquivalentNotes
Section + Row + Column<section> with Tailwind grid/flexUse max-w-7xl mx-auto for container
Fullwidth HeaderHeroSection componentBackground image + overlay + CTA
Blurb ModuleCallToActionCardIcon + title + description card
Accordion / ToggleFAQ component with useStateExpand/collapse with plus/minus SVGs
Testimonial ModuleSwiper carousel or gridQuote marks + author + photo
Contact FormExternal service (Formspree, etc.)Static sites cannot process forms server-side
Number CounterAnimated counter with Framer MotionTrigger on scroll/viewport intersection
Gallery ModuleCSS Grid + WebP imagesResponsive grid with object-cover
Video Module<video> element with posterSelf-hosted MP4 in public/videos/
CTA ModuleTailwind button/card with LinkUse Next.js Link for internal routes

10. CI/CD Pipeline

Automated build, test, scan, and deploy workflows

The FFC template includes 4 GitHub Actions workflows that run automatically:

1

CI Workflow (ci.yml)

Runs on every push and pull request. Installs dependencies, runs linting (ESLint), type checking (TypeScript), unit tests (Jest), and builds the static site. Must pass before merge.

2

CodeQL Analysis (codeql.yml)

GitHub's security scanner. Runs after CI passes. Scans for JavaScript/TypeScript vulnerabilities, injection risks, and insecure patterns.

3

Deploy (deploy.yml)

Runs after CodeQL passes (on main branch only). Builds the production static site and deploys to GitHub Pages. Updates the live website automatically.

4

Lighthouse CI (lighthouse.yml)

Runs after deploy completes. Audits the live site for Performance, Accessibility, Best Practices, and SEO. Target scores: Performance β‰₯ 90, Accessibility β‰₯ 95, Best Practices β‰₯ 90, SEO β‰₯ 90.

Pipeline flow: Push to main β†’ CI (lint + type check + test + build) β†’ CodeQL (security scan) β†’ Deploy (GitHub Pages) β†’ Lighthouse (performance audit)

11. Testing Checklist

Quality gates before deployment

Automated Tests

Manual Verification

12. Deployment and DNS Cutover

Zero-downtime switch from WordPress to Next.js

Step 1: Configure GitHub Pages

  • Add public/CNAME with the charity domain (e.g., charityname.org)
  • Add public/.nojekyll (empty file to disable Jekyll processing)
  • Enable GitHub Pages in repo Settings > Pages > Source: GitHub Actions

Step 2: Set Up Cloudflare Redirects

Configure Cloudflare Bulk Redirects for any changed or dropped URLs:

  • Create a Bulk Redirect List in Cloudflare dashboard
  • Add 301 redirects for each old URL β†’ new URL
  • Enable the Bulk Redirect Rule

Note: Cloudflare is used in DNS-only mode (orange cloud OFF for most records). No CDN caching rules, no Workers, no Page Rules are needed.

Step 3: Staging Test

Before touching DNS, deploy to the GitHub Pages default URL and run full verification. The site should be accessible at https://freeforcharity.github.io/REPO-NAME/

Step 4: DNS Cutover

# In Cloudflare DNS:

# 1. Change the A record (pointing to WordPress/cPanel IP)

DELETE A charityname.org β†’ 66.45.234.13

# 2. Add CNAME to GitHub Pages

ADD CNAME charityname.org β†’ freeforcharity.github.io

# 3. Set proxy status: DNS-only (gray cloud) for DDoS protection

# 4. Wait for GitHub HTTPS certificate provisioning (up to 1 hour)

# 5. Verify all pages at https://charityname.org

Important: Keep the WordPress site running for at least 2 weeks after cutover. This provides a rollback option if any issues are discovered. Do NOT cancel WordPress hosting until the Next.js site is verified and stable.

13. Rollback Procedure

How to revert to WordPress if needed

If critical issues are discovered after cutover, reverting to WordPress takes about 5 minutes:

  1. 1.In Cloudflare DNS, delete the CNAME record pointing to freeforcharity.github.io
  2. 2.Re-create the A record pointing to the WordPress/cPanel IP (e.g., 66.45.234.13)
  3. 3.Wait for DNS propagation (typically 5-30 minutes with Cloudflare)
  4. 4.Verify the WordPress site is accessible at the domain
  5. 5.Investigate and fix the Next.js issue before attempting cutover again

Why this works: The WordPress site is untouched during the entire conversion process. DNS is the only thing that changes. Reverting DNS restores WordPress immediately.

14. FAQ for Charities and Admins

Common questions about the conversion process

Q: Will our website look different after conversion?

The goal is to maintain the same visual appearance and content. The technology changes behind the scenes, but visitors should see the same website. Performance will typically improve significantly.

Q: How do we update content after the conversion?

Content is updated by editing files in the GitHub repository. For simple text changes, you can edit directly on GitHub.com. For more complex changes, clone the repo locally, make changes, and push. The site rebuilds and deploys automatically.

Q: What about our contact forms?

Static sites cannot process form submissions server-side. FFC uses external form services like Formspree or integrates with the charity's existing form provider. The form still works for visitors; it just submits to an external service instead of WordPress.

Q: What about donations?

Donation processing is handled by external payment platforms (Zeffy, PayPal, Stripe, etc.) that the charity already uses. The donation page links to these external services. No payment processing happens on the website itself.

Q: Is there any downtime during the switch?

With proper execution, there is zero downtime. The new site is fully deployed and tested before the DNS switch. DNS changes with Cloudflare propagate in minutes. WordPress stays running as a backup during the transition period.

Q: What does it cost us?

Nothing. The entire stack (hosting, DNS, CI/CD, source control) is free. FFC provides the conversion service at no cost. The only requirement is the mandatory footer attribution.

Q: Can we go back to WordPress if we don't like it?

Yes. FFC keeps the WordPress site running for at least 2 weeks after cutover. Reverting is a 5-minute DNS change. However, most charities prefer the new stack due to better performance and zero maintenance requirements.

Q: Do we need technical staff to manage the new site?

For basic content updates, no. Simple text changes can be made through GitHub's web interface. For more complex changes (adding pages, modifying layout), some familiarity with HTML/React is helpful. FFC volunteers are available to assist with changes.

15. Reference Links

All related repositories, documentation, and tools

FFC Repositories

FFC Admin Documentation

Revision History

DateVersionChanges
February 20261.1Added GitHub push protection fix, request throttling, PAT security guidance, and URL exclusion list for Simply Static exports (Section 5, Steps 4-6)
February 20261.0Initial guide created during FreeForCharity.org conversion project

This guide is maintained at FFC-IN-ffcadmin.org and published at ffcadmin.org/wordpress-to-nextjs-guide