WordPress to Next.js + Directus: Real Production Migration with Code Examples

I spent 12 months running a multi-city event platform on both WordPress Multisite and Next.js + Directus. 400+ content items, 15 independent city sites, multi-tenant permissions. This is what I learned, including the parts that sucked.
Fair warning: this is long. I'm covering performance, security, SEO, and the production problems nobody warns you about with Directus. Skip to whatever section matters to you.
The Migration Context
What We Built:
- Multi-city event platform (15 independent city sites)
- 400+ content items with streaming media
- Event management across 15 independent cities
- Accessibility-first (WCAG 2.1 Level AA required)
- Previous tech: WordPress Multisite
Why We Migrated:
WordPress Multisite was becoming a maintenance burden with plugin conflicts, performance issues, and complex permission management across city teams.
Part 1: WordPress in 2025
What WordPress Does Really Well
1. Content Management is Instant
- Gutenberg editor: visual, intuitive, zero learning curve
- Non-technical editors productive in 5 minutes
- 60,000+ plugins for almost any feature
- Themes for quick design implementation
- Shared hosting: $5-30/month
2. Ecosystem is Unmatched
- SEO plugins (Yoast, Rank Math)
- E-commerce (WooCommerce)
- Forms, galleries, everything you need
- Community support everywhere
- WordPress.com for managed hosting
3. When WordPress Makes Perfect Sense
- Small business websites (5-20 pages)
- Blogs and content-first sites
- Non-technical team managing content
- Budget under $5K
- Need to launch quickly (1-2 weeks)
Where WordPress Struggles
1. Performance Ceiling
- Page loads: 3-5 seconds (even with caching)
- Database queries multiply with plugins
- PHP execution overhead on every request
- Difficult to achieve Lighthouse 90+ scores
Our WordPress Stats:
- Average page load: 4.2 seconds
- Lighthouse performance: 65/100
- Database queries per page: 80-120
- Time to Interactive: 6+ seconds
Industry Benchmarks (2025):
- WordPress average: 2.5s desktop, 13.25s mobile
- Only 57.8% of WordPress sites meet Google's LCP threshold (<2.5s)
- Mobile performance is 5x slower than desktop
- Our 4.2s is on the slower end, typical for multisite with plugins
2. Multisite Complexity
- Plugin compatibility nightmares
- Shared database = performance bottleneck
- Permission management across sites is painful
- Backup/restore complexity
- Updates break things regularly
Multisite Statistics:
- 30% of sites experience conflicts after updates (2023 data)
- Many plugins lack multisite compatibility testing
- Sites with 20+ plugins: 50% higher likelihood of performance issues
- 60% of WordPress issues stem from plugin conflicts
3. Maintenance Burden
- Weekly plugin updates
- Security patches (WordPress is a top attack target)
- PHP version compatibility issues
- Database optimization needed regularly
- Broken site after updates (happened 3 times)
- Regular maintenance required for optimal performance
Security Reality (2023-2024):
- 5,948 new vulnerabilities disclosed (24% increase from 2022)
- 93.25% originate from plugins, only 1.29% from WordPress core
- 31% are Critical/High severity requiring immediate attention
- XSS accounts for 50% of vulnerabilities, CSRF 15%, SQL injection 2%
- 48% of sites run outdated installations (prime attack targets)
- Only 69% of vulnerable plugins get patched; 26% never fixed
When to Stay on WordPress
✅ Non-technical content team (no developers)
✅ Budget under $5K for development
✅ Simple content site (blog, business site)
✅ Need 50+ plugins for features
✅ Shared hosting is sufficient
Part 2: Next.js 15 Production Experience
What Next.js Does Brilliantly
1. Performance is Exceptional
- Server-side rendering: HTML ready instantly
- Static generation: pre-built pages
- Image optimization built-in
- Code splitting automatic
Our Next.js Stats:
- Average page load: <1 second
- Lighthouse performance: 98/100
- Time to Interactive: 1.5 seconds
- First Contentful Paint: 0.4 seconds
Before → After:
- Page loads: 4.2s → 0.8s (5x faster)
- Lighthouse: 65 → 98 (51% improvement)
- Time to Interactive: 6s → 1.5s (4x faster)
Industry Validation:
- Next.js typically 40-60% faster than WordPress (multiple studies)
- Core Web Vitals: LCP 1.8s (Next.js) vs 4.2s (WordPress)
- FID: 45ms (Next.js) vs 280ms (WordPress)
- Mobile Lighthouse scores: +35-68% improvement over WordPress
- Real case (Digital Polygon): 51% mobile → 86% mobile after migration
2. Developer Experience is Modern
- TypeScript integration perfect
- Hot reload during development
- React component architecture
- Great documentation and tooling
- App Router is intuitive (once learned)
3. Scalability Built-In
- Handle 10K+ concurrent users
- Edge deployment (Vercel, Cloudflare)
- API routes for backend logic
- Middleware for authentication
- Multi-tenant architecture possible
Business Impact (Industry Data):
- 15-25% increase in organic traffic after migration (improved CWV = better rankings)
- 20-30% improvement in Core Web Vitals scores
- Typical ROI achieved within 6-12 months
- Lower bounce rates from faster page loads
- Better conversion rates from improved UX
Where Next.js Has Challenges
1. No Plugin Ecosystem
- Build every feature from scratch
- Authentication: code it yourself
- Forms: integrate a service
- SEO: implement metadata manually
- Image galleries: build components
Migration Considerations:
- Significant development time required for complex migrations
- WordPress setup is much faster for standard sites
2. Learning Curve
- React knowledge required
- Server Components vs Client Components
- App Router patterns (different from Pages Router)
- Build tools and configurations
- Deployment understanding needed
3. Hosting Complexity
- Can't use shared hosting
- Need Docker or Vercel/Netlify
- Environment variables management
- Build process required
- CI/CD pipeline needed
- VPS or managed hosting typically more expensive than WordPress shared hosting
Performance Note: Next.js 15 App Router
- App Router can be 2.5x slower for server-heavy apps (vs Pages Router)
- TTFB may be 2x slower for SSR use cases
- Vercel hosting optimizes App Router significantly
- Pages Router still faster for request-per-second workloads
- Choose based on your architecture: App Router for client-heavy, Pages for server-heavy
When Next.js Makes Sense
✅ Developer team available (React/TypeScript)
✅ Performance critical (e-commerce, SaaS)
✅ Multi-tenant or complex architecture
✅ Custom features needed
✅ Budget > $10K for development
Part 3: Directus CMS Real Experience
What Directus Excels At
1. Self-Hosted, Zero Licensing Fees
- Open source, MIT licensed
- Host anywhere (Docker, VPS, cloud)
- Full data ownership
- No per-user or API call fees
Cost Comparison:
- Directus self-hosted: $0/year (free under £3.97M revenue)
- Commercial headless CMS alternatives: $4,800-$6,000+/year
- Savings: $5-6K annually vs managed headless CMS services
2. TypeScript-Native API
- Auto-generated TypeScript types from schema
- Directus SDK (
@directus/sdk) with full type safety - REST and GraphQL APIs both available
- Webhook support for real-time updates
- Perfect Next.js Server Components integration
Real Implementation:
// Auto-generated types from Directus schema
import { EventItems, EventSchedules } from '@/data/directus-collections'
// Type-safe SDK usage
const items = await directusApi.request(
readItems('event_items', {
fields: ['*', 'image.*', 'categories.*'],
filter: { status: { _eq: 'published' } }
})
)
// TypeScript knows exact structure of 'items'
3. Snapshot-Based Schema Management
- Version control your entire schema
- 524KB
snapshot.yamlfile tracks everything - Collections, fields, relations, permissions
- Deploy schema changes via CLI
- Rollback schema if needed
Our Schema:
# Deploy schema from snapshot
directus schema apply ./cms/snapshots/snapshot.yaml
# Export current schema
directus schema snapshot ./cms/snapshots/snapshot.yaml
4. Flexible Data Modeling
- 17 collections in our project
- Relational data (many-to-many, etc.)
- Custom JSON fields for configuration
- No schema limitations
- Foreign key management
Our Directus Collections:
- Content Items, Schedules, Venues (400+ items)
- Cities, Partners, Contributors
- Blocks (26-block page builder system)
- Users with multi-tenant permissions
- Media files with access control
- Total: 1,000+ content items
Where Directus Has Limitations
1. Permission System Complexity
- JSON-based permission rules (not UI-based)
- Multi-tenant permissions require careful planning
- Testing permissions is tedious
- Took 2 days to set up initially
- Required custom validation scripts
Real Permission Challenge:
{
"policy": "Site_CityAdminPolicy",
"collection": "event_items",
"actions": ["create", "read", "update"],
"permissions": {
"_or": [
{ "website": { "id": { "_eq": "$CURRENT_USER.website" }}},
{ "website": { "id": { "_eq": "main" }}}
]
}
}
CityAdmin can see their city's content + main site content
Permission Management Scripts:
# Deploy 156 permission rules
npm run permissions:deploy
# Validate against test users
npm run permissions:validate
# Revert if broken
npm run permissions:revert
2. System Collections Workarounds
- SDK doesn't work for
directus_users,directus_roles - Must use raw REST API
- Less type-safe, more error-prone
- Hit this limitation week 7
The Workaround:
// Can't use SDK for system collections
const response = await fetch(
`${directusUrl}/users`,
{ headers: { Authorization: `Bearer ${token}` }}
)
// Manual type casting required
3. Docker Deployment Gotcha
- Custom plugins DON'T work in Docker
- Email SMTP requires
EMAIL_SMTP_IGNORE_TLS: true - Spent 4 hours debugging email issues
- Changed SMTP port from 465 → 587
- TLS certificates caused failures
What Broke:
# This failed in production
EMAIL_SMTP_PORT: 465
EMAIL_SMTP_SECURE: true
# Had to use this
EMAIL_SMTP_PORT: 587
EMAIL_SMTP_IGNORE_TLS: true
4. Learning Curve
- Not intuitive for non-technical users
- Relational data concepts needed
- Permission rules documentation sparse
- Community smaller than WordPress
- StackOverflow has fewer answers
- Training required for both developers and content editors
Real Production Feedback:
- Success story: Weber (smart grilling app) - 6M sessions, zero downtime, low latency
- Development savings: 4+ months saved vs custom build (reported by teams)
- Support reduction: 3x decrease in technical support needs
- Performance note: Can slow with large datasets and many concurrent users
- Setup complexity: Requires deeper technical understanding vs WordPress
Developer Experience Survey Data:
- 75% of developers use composable architectures (Netlify, 7K respondents)
- 61% ROI increase reported with headless CMS
- 58% time savings in development workflows
- TypeScript integration = fewer bugs, faster development
When Directus Works Best
✅ Developer team available
✅ Want data ownership (no SaaS lock-in)
✅ Complex relational data
✅ Multi-tenant permissions needed
✅ TypeScript project
✅ Need self-hosted CMS with zero licensing costs
Additional Directus Production Challenges (What We Learned)
After 12 months in production with Directus managing 400+ content items and 1,000+ total records, we encountered several limitations that aren't obvious during initial evaluation. Here are the real challenges we faced:
1. Image Upload & Optimization Limitations
The Problem:
- Directus does NOT automatically resize images on upload by default
- Transformations happen on first read, not upload
- Large images (>10MB) silently fail in UI with no clear error message
- No built-in automatic downscaling to prevent storage bloat
What This Means:
- Content editors can upload 8MB images that slow down your site
- You need custom hooks or third-party extensions to auto-resize
- Transformation presets must be whitelisted to prevent abuse
- Manual image optimization required before upload (frustrating for non-technical editors)
Our Solution:
- Built custom image optimization scripts (TinyPNG API + OpenAI for alt text)
- Added file size validation hooks
- Editor training: "Always optimize before upload"
- Required significant development time to implement
2. Default UI Field Configuration Complexity
The Problem:
- Cannot easily set default field visibility for all users
- System fields lack entries in directus_fields by default
- Each user can customize their layout, but admin can't enforce defaults
- No "lock" feature to prevent users from hiding critical fields
What This Means:
- Every new user sees ALL fields (overwhelming for editors)
- Must use Presets & Bookmarks to create default layouts
- System field customization requires database-level modifications
- Training overhead: teaching editors which fields to display
Our Experience:
- Editors initially confused by 30+ fields in content collection
- Created presets for each collection
- Still get support requests: "Where's the status field?"
3. Duplicate Functionality Breaks with Nested Relations
The Problem:
- "Save as Copy" doesn't work reliably with nested relationships
- Duplication creates blank child table entries instead of copying data
- Many-to-many nested relations create duplicates instead of updating
- Multiple relations create very long URLs → 414 error
Real Impact:
- Cannot duplicate items with nested categories/tags (M2M relations)
- Workaround: Add one nested relationship at a time, save after each
- Creating content templates impossible (defeats purpose of duplication)
- Editors manually re-enter relational data (time-consuming, error-prone)
Our Workaround:
- Built custom "clone item" script via API
- Properly handles nested M2M relations
- Required additional development time
4. Extension Ecosystem is Limited & Often Outdated
The Problem:
- Extensions may not be actively maintained after initial release
- No direct update button in Marketplace - must uninstall/reinstall
- Extensions can conflict with each other
- Much smaller ecosystem compared to WordPress (60,000+ plugins)
Reality Check:
- Limited useful extensions available compared to WordPress ecosystem
- Extensions can break after Directus updates
- Custom plugins don't work in Docker (mentioned earlier)
- Often easier to build custom API endpoints than find/fix extensions
Comparison:
- WordPress: Plugin for everything (form builders, SEO, backups, galleries)
- Directus: Build it yourself or use extensions (limited selection, maintenance risk)
5. DB-Driven Configs Cannot Sync Across Environments
The Problem:
- Permissions, flows, presets, dashboards stored in database, not snapshots
- Schema snapshots don't include these dynamic configurations
- No built-in way to sync dev → staging → production
- Manual recreation in each environment is error-prone
What Broke for Us:
- Spent 2 hours recreating 156 permission rules in production
- Flows created in dev not in staging (caught during QA)
- Dashboard layouts different per environment (confusing for editors)
- Presets lost when database refreshed
Solution (Requires External Tool):
- directus-sync CLI tool (third-party, by Tractr)
- Tracks dashboards, flows, permissions, policies, presets
- Requires directus-extension-sync installed on each instance
- Initial setup required but saves time on subsequent deployments
Commands:
# Compare local config with Directus instance
npx directus-sync diff
# Push local config to Directus instance
npx directus-sync push
Alternative:
- directus-extension-schema-sync - another third-party solution
- Export/import via custom scripts
- Manual recreation (not recommended)
6. Admin UI Performance & Stability Issues
The Problem:
- Checkbox Tree fields make UI unresponsive with typing delays
- Performance drops with large file counts (400K files = 45s response, heap crashes)
- 10 requests/sec = 1.5s average response time per request
- Connection pool timeouts with 3-40 concurrent users
Our Production Experience:
- Admin UI lags when 5+ editors logged in simultaneously
- Collections with 400+ items take 3-5 seconds to load
- File library (2,000+ images) noticeably slow
- Occasional "KnexTimeoutError" during peak usage
Symptoms:
- Editor complaints: "Why is it so slow?"
- Long tasks in Chrome DevTools (>50ms)
- JavaScript heap warnings in logs
- Need to refresh page to "unstick" UI
What Helps:
- Limit concurrent admin users (not always possible)
- Paginate large collections (default: 25 items per page)
- Use filters to reduce result sets
- Clear browser cache regularly
- Database connection pool tuning (trial and error)
Still Needs Work:
- UI responsiveness with large datasets
- Better loading states and feedback
- Connection pool management improvements
- More robust error handling
7. Customization Complexity
The Problem:
- Field names and types are immutable after creation
- Custom interfaces require building extensions
- System collection modifications need database-level access
- Documentation can be sparse for advanced customizations
Examples from Our Project:
- Wanted to rename field: had to create new field, migrate data, delete old
- Custom validation rules: built extension (8 hours development)
- Multi-step forms: completely custom frontend component
- Conditional field visibility: required custom interface
Skill Requirements:
- Vue.js knowledge for custom interfaces
- Node.js for hooks and custom endpoints
- Database knowledge for system field modifications
- Docker for extension deployment (if self-hosted)
The Honest Assessment
Directus is powerful but requires:
- Developer team with full-stack skills
- Time investment in tooling (image optimization, sync tools, custom extensions)
- Patience with ecosystem limitations
- Workarounds for production use cases
These aren't deal-breakers, but be prepared:
- Budget 20-30% more dev time for workarounds vs initial estimates
- Factor in maintenance of custom scripts/extensions
- Plan for editor training and ongoing support
- Test thoroughly with production data volumes
Would we still choose Directus?
Yes, for our use case (multi-tenant, complex permissions, TypeScript integration). But we'd budget differently and set clearer expectations with stakeholders about limitations.
When to reconsider Directus:
- Non-technical content team (too many rough edges)
- Need enterprise-grade admin UI polish
- Large concurrent user base (>20 simultaneous editors)
- Require extensive image management (thousands of uploads/month)
- Cannot invest in custom development for workarounds
Part 4: The Migration Reality
Development Timeline
Week 1-2: Foundation
- Set up Next.js 15 + App Router
- Deploy Directus with Docker Compose
- Configure multi-environment setup
- Plan data model (17 collections)
- Set up TypeScript strict mode
Week 3-6: Core Features
- Build 26-block page builder system
- Film catalog with relational data
- Event management + showtimes
- Video streaming integration
- Directus SDK integration
Week 7-10: Multi-Tenant Architecture
- Next.js middleware for city routing
- 156 permission rules (JSON config)
- City-specific data isolation
- Permission testing framework
- Hit system collections SDK limitation
Week 11-12: Migration & Polish
- Import scripts for 400+ films
- Data verification + rollback system
- Content team training (2 hours)
- Launch with reverse proxy fallback
- Email configuration debugging (4 hours)
Total: 12 weeks, 2 developers, 480 hours
Technical Implementation Deep-Dive
1. Directus Schema Snapshots
We version control the entire CMS schema as code:
cms/snapshots/
└── snapshot.yaml # 524KB, 15,000+ lines
What It Includes:
- 17 collections with all fields
- 156 permission rules
- Foreign key relationships
- Display templates and metadata
- Junction tables for many-to-many
Deployment:
# Apply schema to new environment
directus schema apply ./cms/snapshots/snapshot.yaml
# Export after changes
directus schema snapshot ./cms/snapshots/snapshot.yaml
# Version control with git
git commit -m "feat: add streaming access collection"
2. Auto-Generated TypeScript Types
Directus schema auto-generates TypeScript interfaces:
// src/data/directus-collections.d.ts (auto-generated)
export type ReelFilms = {
id: number
title: string
slug: string
synopsis?: string | null
runtime?: number | null
website?: number | ReelsWebsites | null
disabilities: ReelFilmsDataDisabilities[]
topics: ReelFilmsDataTopics[]
image?: string | DirectusFiles | null
status: 'draft' | 'published' | 'archived'
}
Type Safety Benefits:
- Autocomplete in VS Code
- Compile-time error checking
- Refactoring confidence
- Zero type definition maintenance
3. Caching Strategy
Next.js ISR + Directus cache management:
// Default 60-second revalidation
const directusApi = createDirectus(url)
.with(rest({
onRequest: (options) => ({
...options,
next: { revalidate: 60 }
})
}))
// Cache-busting API endpoint
POST /api/cache/clear
→ Clears Next.js cache
→ Clears Directus cache
→ Revalidates specific paths
Cache Clear Script:
npm run cache:clear
# Clears: films, events, pages, navigation
# Revalidates: /, /films, /events, city pages
4. Image Optimization Pipeline
Automated image processing:
// scripts/optimize-image.ts
1. Fetch image from Directus
2. Optimize with TinyPNG API (< 1MB)
3. Generate alt text with OpenAI GPT-4
4. Upload back to Directus
5. Update metadata
// Bulk optimization
npm run images:optimize
npm run images:describe
Results:
- Average image size: 2.5MB → 400KB (84% smaller)
- Automated alt text generation
- WCAG 2.1 AA compliance
5. Data Management Scripts
Import/export automation:
# Film import with verification
npm run films:import -- --csv=./films.csv
npm run films:verify # Check for issues
npm run films:revert # Rollback if needed
# Stream video import
npm run streams:import -- --csv=./videos.csv
# Website duplication (new city)
npm run duplicate -- --from=houston --to=cleveland
Import Features:
- CSV validation
- Duplicate detection
- Fuzzy matching (Video IDs)
- Transaction rollback
- Progress tracking
What We Gained
Performance:
- 5x faster page loads
- Better SEO rankings
- Improved user experience
- Lower bounce rates
Maintainability:
- Fewer breaking changes
- Type safety catches errors
- Better code organization
- Easier to add features
Scalability:
- Multi-tenant from scratch
- Can handle 10x traffic
- Easy to add new cities
- Custom features possible
What We Lost
Ease of Use:
- No visual page builder
- More technical setup
- Requires developers
- Longer feature development
Plugin Ecosystem:
- Build features from scratch
- No quick SEO plugin install
- Custom forms needed
- Integration work required
Challenges We Actually Hit (and Solutions)
Challenge 1: Docker Email Configuration
- Problem: Directus emails failing in production
- Root Cause: TLS certificate validation
- Time Lost: 4 hours debugging
- Solution:
EMAIL_SMTP_IGNORE_TLS: true+ port 587 - Lesson: Test email in staging first
Challenge 2: Permission Testing
- Problem: No UI to test 156 permission rules
- Impact: Manual testing took hours
- Solution: Built automated test suite
// src/lib/services/permissions/scripts/validate.ts
- Creates test users (MainAdmin, CityAdmin)
- Tests CRUD operations per collection
- Validates multi-tenant isolation
- Reports failures with details
- Result: 5-minute validation vs 2-hour manual test
Challenge 3: SDK Limitations for System Collections
- Problem:
directus_usersSDK doesn't work - Discovery: Week 7 (painful timing)
- Workaround: Raw REST API calls
// Lost type safety here
const users = await fetch(`${url}/users`, {
headers: { Authorization: `Bearer ${token}` }
})
- Lesson: Read SDK limitations docs early
Challenge 4: Image Optimization at Scale
- Problem: 400+ images, 2.5MB average
- Manual effort: 40 hours estimated
- Solution: Automated pipeline
- TinyPNG API for compression
- OpenAI for alt text generation
- Batch processing scripts
- Time saved: 38 hours (95% reduction)
Challenge 5: Multi-Tenant Data Leaks
- Problem: City A seeing City B's content
- Root Cause: Missing
websitefilter - Detection: Permission validation tests
- Fix: Required filters in all queries
// Every query needs this
filter: {
website: { id: { _eq: currentWebsite }}
}
- Prevention: Automated test suite
Challenge 6: Snapshot Conflicts
- Problem: Schema changes cause merge conflicts
- File:
snapshot.yaml(15,000 lines) - Frequency: 2-3 times during development
- Solution: Manual merge + reapply
- Lesson: Coordinate schema changes
Industry Migration Case Studies
Smashing Magazine: 800ms → 80ms (10x Faster)
- Challenge: 20K+ comments, thousands of articles, traffic spikes
- Result: HTML load time improved from 800ms to 80ms
- Benefit: Consistent performance during traffic spikes
- Tech: WordPress to JAMstack
- Key win: Better security, no performance degradation under load
10Clouds: +25% Traffic from Performance
- Challenge: Complex agency site with portfolio, blog, case studies
- Result: 25% increase in website traffic post-migration
- Benefits: Simplified architecture, more team autonomy
- Tech: WordPress to JAMstack with headless CMS
- Key win: Faster, more customizable, easier maintenance
LiveChat: Developer Focus Restored
- Challenge: Sysadmins supporting marketing pages instead of core product
- Problem: Plugin security concerns, caching challenges
- Timeline: Started 2017, flagship site migrated November 2019
- Result: Smooth transition, better analytics tracking
- Tech: WordPress to JAMstack with Netlify
- Key win: Team could focus on core product instead of WordPress maintenance
Localcoin: Better UX = Business Growth
- Challenge: Slow page speeds, dated tech stack hurting conversions
- Impact: Poor UX slowing business growth and customer satisfaction
- Result: Significantly reduced page weight and improved performance
- Tech: WordPress to Next.js
- Key win: Improved customer satisfaction and conversion rates
Common Migration Success Patterns:
- Performance improvements: 5-10x faster load times
- Maintenance reduction: 50-75% less time on updates/patches
- Traffic increases: 15-25% from better SEO (Core Web Vitals)
- Developer productivity: 40-60% faster feature development
- Team focus: More time on product, less on infrastructure
Common Migration Timeline:
- Small site (5-20 pages): 2-4 weeks
- Medium site (50-100 pages): 6-12 weeks
- Large/complex site (100+ pages, custom features): 12-24 weeks
- Enterprise migration: 6-12 months
Part 5: Technology Decision Framework
Choose WordPress If:
- Non-technical team managing content
- Budget under $5K
- Simple content site (blog, portfolio, business)
- Need quick launch (1-4 weeks)
- Want 60,000+ plugins available
- Shared hosting sufficient
- Visual page builder required
Choose Next.js + Directus If:
- Have developer team (React/TypeScript)
- Budget > $10K
- Performance critical (Core Web Vitals)
- Multi-tenant application
- Complex custom features
- Want data ownership (self-hosted)
- Long-term scalability important
- TypeScript project
Choose Next.js + Managed Headless CMS If:
- Want managed CMS service (no self-hosting)
- Budget > $20K/year
- Need enterprise support
- Don't want to manage infrastructure
- Prefer SaaS solution over self-hosted
Part 6: SEO Performance Comparison
WordPress SEO: Strengths
What WordPress Does Well:
- Quick setup: Yoast SEO and Rank Math plugins handle meta tags, sitemaps, schema
- User-friendly: Non-technical editors can optimize content
- Mature ecosystem: Extensive plugins for every SEO need
- Good for small business: Get started within hours with proven tools
- Established patterns: Most SEO agencies know WordPress well
The "Google Loves WordPress" Myth:
⚠️ Truth: WordPress is SEO-agnostic. Google doesn't favor WordPress. What matters:
- Theme quality and code implementation
- Site speed and Core Web Vitals
- Content quality and structure
- Technical implementation
Next.js SEO: Strengths
Why Next.js Excels at SEO:
- Server-side rendering: Content ready for crawlers (no client-side rendering delays)
- Static generation: Pre-rendered HTML = instant crawling
- Performance = ranking factor: Core Web Vitals directly impact rankings
- Built-in optimizations: Lazy loading, code splitting, image optimization
- Structured sitemaps: Full control over sitemap generation
- Middleware: Advanced control over headers, redirects, canonical URLs
Performance = SEO Advantage:
- Core Web Vitals are now ranking factors (since 2021)
- Next.js excels: LCP <2.5s, FID <100ms, CLS <0.1
- WordPress struggles: Especially on mobile (13.25s avg load time)
Real SEO Impact: The Data
Next.js After Migration:
- 15-25% increase in organic traffic (multiple case studies)
- 20-30% improvement in Core Web Vitals scores
- Mobile PageSpeed scores: +35-68% improvement
- Better mobile rankings (Google is mobile-first indexing)
- Lower bounce rates from faster loads
WordPress Mobile Challenge:
- Desktop performance often good (2.5s average)
- Mobile performance poor (13.25s average) - 5x slower
- Only 57.8% of WordPress sites meet LCP threshold
- Mobile performance gap hurts rankings
SEO Migration Best Practices
Critical SEO Tasks When Migrating:
-
301 Redirects (NON-NEGOTIABLE)
- Map every WordPress URL to Next.js URL
- Test all redirects before launch
- Monitor 404 errors in Google Search Console
- Use next.config.ts for permanent redirects
-
Preserve URL Structure (if possible)
- Keep same slugs and hierarchy
- If changing URLs, ensure 301 redirects
- Update internal links
-
Sitemap Updates
- Generate new sitemap.xml
- Submit to Google Search Console
- Include all pages, posts, dynamic routes
- Set proper priorities and update frequencies
-
Meta Tags & Schema
- Migrate all meta descriptions
- Port schema markup (JSON-LD)
- Ensure Open Graph tags
- Twitter Card meta tags
-
Image Optimization
- Optimize all images (<1MB)
- Add descriptive alt text
- Use Next.js Image component
- Implement lazy loading
-
Monitor Rankings
- Track rankings before migration (baseline)
- Watch Google Search Console closely
- Expect temporary fluctuations (2-4 weeks)
- Most sites see improvement after 4-8 weeks
The Verdict: Which is Better for SEO?
WordPress for SEO if:
- Small business, quick start
- Non-technical team
- Need plugin simplicity
- Budget under $5K
Next.js for SEO if:
- Performance critical (e-commerce, SaaS)
- Competitive keywords (every ms counts)
- Mobile-first strategy
- Developer resources available
- Long-term SEO investment
Reality Check:
Both can rank well with proper implementation. However:
- Next.js has performance advantage (faster = better rankings)
- WordPress has easier initial setup (plugins handle basics)
- Next.js requires custom SEO implementation (more control, more work)
- WordPress mobile performance is a real issue (5x slower than desktop)
Bottom Line:
If you can build it, Next.js will outperform WordPress on SEO purely from performance gains. But WordPress + optimization can also rank well if mobile performance is addressed.
Looking back after 12 months
Would I do this migration again? Yes, but I'd plan differently.
The performance gains were real. Going from 4+ second loads to under a second changed how the site felt. SEO improved. Users stopped complaining about slow pages.
But Directus took more work than expected. The TypeScript integration is genuinely great - I still think it's the right choice for developer teams. The permission system, once you understand it, is powerful. But the rough edges (image handling, UI lag with multiple editors, that damn email configuration) added weeks to the timeline.
If I were advising someone today:
Stick with WordPress if you don't have developers, your budget is tight, or your site is straightforward. WordPress isn't bad - it's battle-tested and your content team already knows it. The plugin ecosystem solves problems that would take you weeks to build.
Consider this stack if you have React/TypeScript developers, performance matters for your business, or you're building something complex like multi-tenant. The migration cost pays off in maintainability and speed, but only if you have the team to support it.
What I'd do differently: Budget 30% more time for Directus workarounds. Set up the image optimization pipeline on day one. Test email in staging before anyone asks "why aren't notifications working?" And coordinate schema changes carefully - merge conflicts in a 15,000-line YAML file are not fun.
The tech stack that worked for us: Next.js 15 with App Router, Directus 11.4 self-hosted in Docker, auto-generated TypeScript types, ISR caching, and a VPS at $50/month running all 15 city sites. Details on each piece are scattered throughout this post if you need them.