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.js | Astro | Hugo | |
|---|---|---|---|
| Best for | Interactive apps, dynamic features, API routes | Content sites, marketing, blogs, docs | Large content sites, fastest builds |
| JavaScript shipped | React runtime (~80-200KB) on every page | Zero by default (add as needed) | Zero |
| Content model | Flexible (MDX, API, database, CMS) | Built-in content collections (Zod schemas) | Frontmatter + Go templates |
| Server rendering | SSR, SSG, ISR, Streaming | Static by default, SSR optional | Static only |
| Hosting cost | $0-$20/mo (Vercel), more at scale | $0/mo (static hosting) | $0/mo (static hosting) |
| Lighthouse performance | 80-95 typical | 95-100 typical | 95-100 typical |
| React required? | Yes | No (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:
- Verify JSON:API is enabled: visit
https://your-site.com/jsonapi - Open Claude Code, Cursor, Windsurf, or Cline
- 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.”
- 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.”
- 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:
- Export Drupal content to JSON/CSV
- Upload to Bolt.new, v0.dev, Lovable, or Replit
- “Build a Next.js site with this content. Include a blog listing page, individual post pages, and an about page.”
- Iterate on the design visually
- 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):
npx create-next-app@latest my-site --typescript --app- Audit Drupal content types, fields, taxonomies, and Views
- Export content via JSON:API, database queries, or Views Data Export
- Create TypeScript interfaces for each content type
- Convert exported content to MDX files with frontmatter
- Download media from
sites/default/files/ - Build React components for page layouts
- Implement
generateStaticParams()for content pages - Build listing/archive pages to replace Drupal Views
- Set up redirects in
next.config.jsfor URL changes - Deploy to Vercel
Headless Drupal + Next.js frontend:
- Install and configure JSON:API on your Drupal site (often already enabled)
- Install JSON:API Extras for API customization
- Configure CORS headers for your Next.js domain
npx create-next-app@latest my-frontend --typescript --app- Build a data fetching layer that queries Drupal’s JSON:API
- Create React components for each content type
- Use ISR (
revalidate) for content that changes periodically - Deploy the Next.js frontend to Vercel
- Keep Drupal running as the backend
Drupal features and their Next.js equivalents
| Drupal Feature | Next.js Equivalent | Complexity |
|---|---|---|
| Content types + fields | TypeScript interfaces + MDX/JSON content files | Low |
| Views (listings, archives) | generateStaticParams() + page components | Low |
| Taxonomy | Tags in frontmatter + generated tag pages | Low |
| Blocks | React components | Low |
| Paragraphs (nested content) | React component composition | Medium |
| Entity references | Denormalized data or cross-references | Medium |
| Webforms | React forms + API routes (or third-party like Formspree) | Low-Medium |
| Media entities | public/ directory + next/image | Low |
| Search | Algolia, Meilisearch, or client-side (Pagefind) | Medium |
| User authentication | NextAuth.js, Clerk, Auth0 | Medium |
| Content moderation | External CMS workflow or custom admin | High |
| Multilingual | next-intl or next-i18next + translated content files | Medium-High |
| Cron/scheduled tasks | Vercel Cron, external scheduler, or ISR revalidation | Low |
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 Upgrade | D7 to Next.js Migration | |
|---|---|---|
| Timeline | 3-6 months | Days (AI agent) to weeks (manual) |
| Cost | $50,000-$200,000 | $0-$10,000 (varies by approach) |
| Result | Modern Drupal (still PHP, still database) | JavaScript codebase (React, Node.js) |
| Hosting cost after | $100-$500/mo (managed Drupal) | $0-$20/mo (Vercel/Cloudflare) |
| Maintenance after | Ongoing (Drupal patches, module updates) | Low (npm updates, no server-side patching) |
| Developer pool | Drupal specialists ($50-$200/hr) | JavaScript developers + AI agents |
| Content editing | Drupal admin UI (polished, familiar) | Headless CMS, MDX files, or custom admin |
| Dynamic features | Drupal modules | Next.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…