By Shyam Verma

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

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):

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:

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):

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:

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):

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

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.yaml file 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:

Developer Experience Survey Data:

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:

What This Means:

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:

What This Means:

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:

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:

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:

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):

Commands:

# Compare local config with Directus instance
npx directus-sync diff

# Push local config to Directus instance
npx directus-sync push

Alternative:

6. Admin UI Performance & Stability Issues

The Problem:

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:

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_users SDK 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 website filter
  • 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:

  1. Performance improvements: 5-10x faster load times
  2. Maintenance reduction: 50-75% less time on updates/patches
  3. Traffic increases: 15-25% from better SEO (Core Web Vitals)
  4. Developer productivity: 40-60% faster feature development
  5. 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:

  1. 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
  2. Preserve URL Structure (if possible)

    • Keep same slugs and hierarchy
    • If changing URLs, ensure 301 redirects
    • Update internal links
  3. Sitemap Updates

    • Generate new sitemap.xml
    • Submit to Google Search Console
    • Include all pages, posts, dynamic routes
    • Set proper priorities and update frequencies
  4. Meta Tags & Schema

    • Migrate all meta descriptions
    • Port schema markup (JSON-LD)
    • Ensure Open Graph tags
    • Twitter Card meta tags
  5. Image Optimization

    • Optimize all images (<1MB)
    • Add descriptive alt text
    • Use Next.js Image component
    • Implement lazy loading
  6. 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.

Interested in Collaborating?

Whether it's a startup idea, a technical challenge, or a potential partnership—let's have a conversation.

20+
Years Experience
150K+
Websites Powered
2
Successful Exits
7x
Faster with AI