guide

Migrate Your Drupal Site to Next.js

Drupal and Next.js occupy similar territory in some ways — both handle complex content sites, both support server-side rendering, both have large ecosystems. But they represent fundamentally different technology stacks: PHP vs JavaScript, relational database vs flexible data layer, Drupal’s module system vs npm packages.

If you are considering migrating from Drupal to Next.js, the question is not whether Next.js is “better” than Drupal. Both are mature, capable platforms. The question is whether your site’s needs align better with a JavaScript framework and its ecosystem, and whether the migration cost is justified by the long-term benefits.

This guide covers the honest case for Next.js, the case for headless Drupal as an intermediate option, the practical migration approaches, and the situations where Drupal remains the right choice.

When Next.js is the right target

Next.js makes sense for Drupal migrations when:

Your site needs dynamic features. User authentication, personalized content, real-time data, interactive dashboards, complex forms with server-side validation, API routes. Drupal can do all of this, but Next.js does it in a language (JavaScript/TypeScript) with a much larger developer talent pool and AI tooling ecosystem.

You want to hire JavaScript developers. The JavaScript/TypeScript developer market is significantly larger than the Drupal/PHP specialist market. Drupal developers command $50-$200/hour and are increasingly difficult to find, especially for legacy D7 sites. JavaScript developers are more abundant and AI coding agents (Claude Code, Cursor, Windsurf) work natively with JavaScript projects.

You are building a web application, not just a website. Drupal started as a CMS and grew toward application features. Next.js started as a web framework and handles both content and application use cases. If your Drupal site has significant interactive functionality — member portals, e-commerce, booking systems, data visualization — Next.js is a natural fit.

You want modern deployment and hosting. Vercel, Cloudflare, and Netlify provide deployment platforms purpose-built for Next.js with features like edge rendering, serverless functions, and preview deployments on every pull request. Drupal hosting (Acquia, Pantheon, Platform.sh) is capable but more expensive and more specialized.

When Next.js is NOT the right target

Be honest about this: most Drupal sites are content sites. Marketing pages, blogs, news sites, institutional homepages, documentation. If your Drupal site falls into this category and you do not need dynamic features, Next.js is more framework than you need.

For pure content sites, a static site generator like Astro or Hugo is simpler:

  • Astro ships zero JavaScript by default (Next.js ships the React runtime on every page)
  • Astro has built-in content collections with typed schemas (Next.js requires more setup for content)
  • Astro generates fully static HTML (Next.js defaults to server rendering, static export is an option)
  • Astro costs $0/month to host (Next.js can cost $0-$20/month on Vercel, more at scale)
Next.jsAstroHugo
Best forInteractive apps, dynamic features, API routesContent sites, marketing, blogs, docsLarge content sites, fastest builds
JavaScript shippedReact runtime (~80-200KB) on every pageZero by default (add as needed)Zero
Content modelFlexible (MDX, API, database, CMS)Built-in content collections (Zod schemas)Frontmatter + Go templates
Server renderingSSR, SSG, ISR, StreamingStatic by default, SSR optionalStatic only
Hosting cost$0-$20/mo (Vercel), more at scale$0/mo (static hosting)$0/mo (static hosting)
Lighthouse performance80-95 typical95-100 typical95-100 typical
React required?YesNo (supports React as opt-in island)No

If you are migrating a Drupal marketing site with a blog to Next.js, you are likely adding complexity you do not need. Consider Astro or Hugo first. Reserve Next.js for sites that genuinely need its dynamic capabilities.

The headless Drupal option

Before migrating away from Drupal entirely, consider whether headless Drupal (also called “decoupled Drupal”) makes sense for your situation.

How it works: Keep Drupal as the backend CMS and content API. Build a Next.js frontend that fetches content from Drupal’s JSON:API at build time (Static Site Generation) or request time (Server-Side Rendering). Content editors continue using Drupal’s familiar admin interface. The frontend is modern JavaScript.

When headless Drupal makes sense:

  • Your content team is deeply invested in Drupal’s editing experience
  • You have complex editorial workflows (content moderation, scheduled publishing) that Drupal handles well
  • You need Drupal’s granular permissions (roles, content-type-level access control)
  • You have significant custom Drupal modules providing business logic
  • Your content changes frequently and editors need real-time preview

When headless Drupal does NOT make sense:

  • Your content changes weekly or monthly (over-engineering)
  • You want to eliminate Drupal hosting costs (you still pay for the Drupal backend)
  • You want to stop patching Drupal (security obligations remain)
  • Your content model is simple enough that markdown files would suffice

The cost reality of headless Drupal:

  • You still run Drupal (hosting: $100-$500/month, maintenance: $3,600-$24,000/year)
  • You add a Next.js frontend (hosting: $0-$20/month, development: additional cost)
  • You now maintain two systems instead of one
  • You need developers who understand both Drupal and Next.js (expensive intersection)

Headless Drupal is genuinely the right architecture for large organizations with complex content operations, frequent publishing, and editorial teams that depend on Drupal’s UI. For everyone else, it tends to be over-engineering. If your content changes monthly and your editors could learn a simpler tool, skip the headless approach and migrate fully to Next.js (or Astro) with content in files or a lightweight headless CMS like Sanity, Contentful, or Decap CMS.

Drupal’s content architecture and how it maps to Next.js

Understanding how Drupal concepts translate to Next.js helps you plan the migration:

Content types to TypeScript interfaces and pages

A Drupal content type like “Case Study” with fields for title, body, client name, industry, featured image, and related services becomes:

// types/content.ts
interface CaseStudy {
  title: string;
  slug: string;
  body: string;
  clientName: string;
  industry: string;
  featuredImage: string;
  relatedServices: string[];
  publishDate: string;
}

Content is stored as MDX files, JSON files, or fetched from a headless CMS. In the App Router:

// app/case-studies/[slug]/page.tsx
import { getCaseStudy, getAllCaseStudySlugs } from '@/lib/content';

export async function generateStaticParams() {
  return getAllCaseStudySlugs().map(slug => ({ slug }));
}

export default async function CaseStudyPage({ params }) {
  const study = await getCaseStudy(params.slug);
  return <CaseStudyTemplate {...study} />;
}

Views to generated pages

Drupal Views — listing pages, archives, filtered content displays — become either:

  • Static generation with generateStaticParams() for build-time pages
  • Server components that query content at request time
  • Client components with filtering and search for interactive displays

A “Blog Archive” View in Drupal that shows posts filtered by category and sorted by date becomes a Next.js page with the same logic in JavaScript rather than Drupal’s Views UI.

Taxonomy to tags and filters

Drupal taxonomy vocabularies map to content metadata. In Next.js, taxonomy terms become either:

  • Frontmatter values in MDX files
  • Fields in a headless CMS
  • Data in JSON files

Tag pages are generated with generateStaticParams():

// app/tags/[tag]/page.tsx
export async function generateStaticParams() {
  const allTags = await getAllTags(); // Extract unique tags from content
  return allTags.map(tag => ({ tag }));
}

Paragraphs to React components

If your Drupal site uses the Paragraphs module, content is structured as typed paragraph items rather than a single body field. This maps naturally to React components:

// Each Drupal paragraph type becomes a React component
function PageSections({ sections }) {
  return sections.map((section, i) => {
    switch (section.type) {
      case 'hero': return <HeroSection key={i} {...section} />;
      case 'text_with_image': return <TextWithImage key={i} {...section} />;
      case 'testimonial_carousel': return <Testimonials key={i} {...section} />;
      case 'cta_banner': return <CTABanner key={i} {...section} />;
      default: return null;
    }
  });
}

The Paragraphs-to-components pattern is actually one of the cleanest mappings in the migration. Drupal Paragraphs are essentially a component system stored in a database. React components are a component system stored in code.

Entity references

Drupal’s entity reference fields (an Event references a Venue, a Product references a Category) need careful handling. In Next.js, you have options:

  • Denormalize at build time: Resolve all references during content extraction and embed the referenced data in each content file
  • Cross-reference at render time: Keep references as IDs and resolve them in your page components
  • Use a headless CMS: Let the CMS handle the relationship layer (Sanity, Contentful, and Strapi all support references)

Migration approaches: every option

1. AI coding agents (Claude Code, Cursor, Windsurf, Cline)

The most effective approach for developers. Drupal’s JSON:API (D8+) is excellent, and AI agents can consume it directly to build a Next.js project.

D8+ with JSON:API:

  1. Verify JSON:API is enabled: visit https://your-site.com/jsonapi
  2. Open Claude Code, Cursor, Windsurf, or Cline
  3. For a full migration away from Drupal:

“Fetch all published content from my Drupal site at example.com using JSON:API. For each content type, create a TypeScript interface and a corresponding content directory with MDX files. Download all media. Build a Next.js 14 project with App Router, using generateStaticParams for all content pages. Create React components for the main page layouts. Preserve URL aliases as routes.”

  1. For a headless Drupal setup:

“Build a Next.js 14 frontend that fetches content from my Drupal site’s JSON:API at example.com. Create TypeScript types matching the Drupal content types. Build page components for each content type. Use generateStaticParams with ISR (revalidate every 60 seconds) for content pages.”

  1. The agent builds the project, you review and iterate

D7 (database export):

Drupal 7 lacks JSON:API. Export via SQL:

-- Articles with all fields
SELECT n.nid, n.title, n.created, n.changed,
       b.body_value, b.body_summary,
       ua.alias
FROM node n
JOIN field_data_body b ON b.entity_id = n.nid AND b.entity_type = 'node'
LEFT JOIN url_alias ua ON ua.source = CONCAT('node/', n.nid)
WHERE n.type = 'article' AND n.status = 1;

-- Custom fields (each field has its own table in D7)
SELECT entity_id, field_subtitle_value
FROM field_data_field_subtitle
WHERE entity_type = 'node';

Export to JSON or CSV, then feed to the AI agent.

Real-world precedent: Sites like cursor.com and prefect.io have used AI agents for site migrations. The pattern — extract content via API, have the agent scaffold the new project — is well-established and works especially well with Drupal’s strong API layer.

2. AI app builders (Bolt.new, v0.dev, Lovable, Replit Agent)

For teams that prefer a visual approach:

  1. Export Drupal content to JSON/CSV
  2. Upload to Bolt.new, v0.dev, Lovable, or Replit
  3. “Build a Next.js site with this content. Include a blog listing page, individual post pages, and an about page.”
  4. Iterate on the design visually
  5. Download or deploy the result

These tools generate React/Next.js code and can produce a working site quickly. They work best for simpler content sites. For complex Drupal sites with many content types, Paragraphs, and entity references, a coding agent gives you more precision.

3. Hire a Drupal migration specialist

The Drupal community has deep migration expertise:

  • Freelancers: $2,000-$10,000 for typical migrations. They understand Drupal’s entity system, field storage, and content extraction thoroughly.
  • Agencies: $10,000-$50,000 for enterprise migrations with complex content models, multilingual content, or custom module logic that needs replication.
  • Headless Drupal specialists: Some developers specialize specifically in Drupal-as-backend + JavaScript-frontend architectures. They can set up the headless approach with proper API configuration, JSON:API Extras module for customized endpoints, and Next.js data fetching patterns.

Find them on Drupal.org marketplace, Drupal Slack, Toptal, or through Drupal agencies like Lullabot, Acquia partners, or Chapter Three.

4. BrowserCat Migrate (automated)

BrowserCat Migrate crawls your Drupal site and rebuilds it as a static site project (Astro-based) with a GitHub repo. The output can serve as a starting point for a Next.js migration — the extracted content and assets transfer between frameworks.

5. Drupal-native export tools

Drupal’s content extraction options are genuinely excellent:

  • JSON:API (D8+ core): Full RESTful API with relationships, filtering, pagination, sparse fieldsets. The best CMS API for migration.
  • JSON:API Extras (contrib): Customize JSON:API output — rename fields, disable resources, configure includes. Useful for cleaner API responses.
  • Views Data Export (contrib): Export any View as CSV, JSON, or XML.
  • Migrate module (D8+ core): Drupal’s migration framework. Can export to custom destinations.
  • GraphQL module (contrib): If you prefer GraphQL over REST, Drupal has a mature GraphQL module. Next.js works well with GraphQL via Apollo Client or urql.
  • Drush: Command-line exports, database dumps, entity queries.

6. Manual DIY

Full migration to Next.js (content in files):

  1. npx create-next-app@latest my-site --typescript --app
  2. Audit Drupal content types, fields, taxonomies, and Views
  3. Export content via JSON:API, database queries, or Views Data Export
  4. Create TypeScript interfaces for each content type
  5. Convert exported content to MDX files with frontmatter
  6. Download media from sites/default/files/
  7. Build React components for page layouts
  8. Implement generateStaticParams() for content pages
  9. Build listing/archive pages to replace Drupal Views
  10. Set up redirects in next.config.js for URL changes
  11. Deploy to Vercel

Headless Drupal + Next.js frontend:

  1. Install and configure JSON:API on your Drupal site (often already enabled)
  2. Install JSON:API Extras for API customization
  3. Configure CORS headers for your Next.js domain
  4. npx create-next-app@latest my-frontend --typescript --app
  5. Build a data fetching layer that queries Drupal’s JSON:API
  6. Create React components for each content type
  7. Use ISR (revalidate) for content that changes periodically
  8. Deploy the Next.js frontend to Vercel
  9. Keep Drupal running as the backend

Drupal features and their Next.js equivalents

Drupal FeatureNext.js EquivalentComplexity
Content types + fieldsTypeScript interfaces + MDX/JSON content filesLow
Views (listings, archives)generateStaticParams() + page componentsLow
TaxonomyTags in frontmatter + generated tag pagesLow
BlocksReact componentsLow
Paragraphs (nested content)React component compositionMedium
Entity referencesDenormalized data or cross-referencesMedium
WebformsReact forms + API routes (or third-party like Formspree)Low-Medium
Media entitiespublic/ directory + next/imageLow
SearchAlgolia, Meilisearch, or client-side (Pagefind)Medium
User authenticationNextAuth.js, Clerk, Auth0Medium
Content moderationExternal CMS workflow or custom adminHigh
Multilingualnext-intl or next-i18next + translated content filesMedium-High
Cron/scheduled tasksVercel Cron, external scheduler, or ISR revalidationLow

The Drupal 7 to Next.js path

D7 sites face a forced migration (end of life January 2025). The D7-to-Next.js path has specific considerations:

D7 lacks JSON:API. You must export via database queries, the Services module, Views Data Export, or site crawling. Database export is the most reliable.

D7 field storage is different. Each field has its own field_data_* table. Extracting a content type with 10 custom fields requires joining 10+ tables. AI agents can handle this if you provide the database schema, but it is more complex than D8+ extraction.

D7 custom modules are PHP. If your D7 site has custom modules with business logic (custom entity types, custom forms, hook implementations), that logic needs to be reimplemented in JavaScript. An AI coding agent can often translate PHP logic to TypeScript, but complex Drupal-specific patterns (render arrays, hook system, entity API) require understanding of both worlds.

D7 themes are PHPTemplate. D7 theme templates (.tpl.php files) do not translate directly to React components, but the structure (what data appears where) provides a blueprint for building React components.

The upgrade comparison

D7 to D10 UpgradeD7 to Next.js Migration
Timeline3-6 monthsDays (AI agent) to weeks (manual)
Cost$50,000-$200,000$0-$10,000 (varies by approach)
ResultModern Drupal (still PHP, still database)JavaScript codebase (React, Node.js)
Hosting cost after$100-$500/mo (managed Drupal)$0-$20/mo (Vercel/Cloudflare)
Maintenance afterOngoing (Drupal patches, module updates)Low (npm updates, no server-side patching)
Developer poolDrupal specialists ($50-$200/hr)JavaScript developers + AI agents
Content editingDrupal admin UI (polished, familiar)Headless CMS, MDX files, or custom admin
Dynamic featuresDrupal modulesNext.js API routes, React components, npm packages

The D10 upgrade is the right choice if your organization has Drupal expertise, your site genuinely needs Drupal’s capabilities, and the budget supports it. Drupal 10 is excellent software with a strong future. The migration to Next.js is the right choice if you want to move to the JavaScript ecosystem, your site’s dynamic needs align with what Next.js provides, and you want broader access to developer talent and AI tooling.

After the migration

Whether you went fully static or headless Drupal + Next.js:

  • Full migration: Your content lives in files (MDX, JSON) or a lightweight headless CMS. No Drupal to maintain. Any JavaScript developer or AI agent can modify the site. Hosting is $0-$20/month.
  • Headless Drupal: Content editors use Drupal’s familiar interface. The frontend is modern React. You still maintain Drupal, but the frontend is now a JavaScript project that any React developer can work on. The API boundary gives you flexibility to replace Drupal with another headless CMS later.

In both cases, you gain access to the React/Next.js ecosystem — npm packages, component libraries, AI coding agents, and a large developer community. The tradeoff is leaving behind Drupal’s integrated module ecosystem and its dedicated (if smaller) community of specialized developers.

The Drupal community may view leaving the platform as a loss. Acknowledge their perspective — Drupal is genuinely powerful software that has served the web for over two decades. The migration to Next.js is not a referendum on Drupal’s quality. It is a practical decision about which technology stack best serves your site’s needs and your team’s capabilities going forward.

Automate Everything.

Tired of managing a fleet of fickle browsers? Sick of skipping e2e tests and paying the piper later?

Sign up now for free access to our headless browser fleet…

Get started today!