Migrate Your Drupal Site to Raw Code
A Drupal site is not just a website. It is a PHP application stack: Drupal core (thousands of PHP files), contributed modules (dozens to hundreds more), a MySQL or PostgreSQL database with hundreds of tables, a web server configuration, a caching layer, and a deployment pipeline that requires Drush commands, config imports, database updates, and cache rebuilds. The codebase of a typical Drupal 8+ site with Composer-managed dependencies can exceed 50,000 PHP files before you write a single line of custom code.
All of this infrastructure exists to serve web pages. For complex web applications with user accounts, permissions, and workflows, that infrastructure earns its keep. But for sites that serve the same HTML to every visitor — marketing sites, blogs, business homepages, portfolios — it is a tremendous amount of machinery for a straightforward job.
Converting a Drupal site to raw code means replacing that machinery with something you can actually read and understand: a project directory with hundreds of files instead of tens of thousands, where every file is content or presentation logic that you authored, and where deployment is git push instead of a multi-step Drush ritual.
This guide is about that simplification — moving from a Drupal installation you administer to a codebase you own — and all the practical approaches for getting there.
What “owning the code” actually means
When your Drupal site becomes a code project, here is what the file structure looks like:
my-company-site/
src/
pages/
index.astro # Homepage
about.astro # About page
services/
consulting.astro # Service pages
development.astro
blog/
[slug].astro # Dynamic route for blog posts
content/
blog/
redesigning-our-office.md # Was a Drupal Article node
company-retreat-2025.md # Body text + frontmatter fields
news/
funding-announcement.md # Was a custom content type
team/
jane-doe.md # Was a "Team Member" node
components/
Header.astro # Was a Drupal menu + block
Footer.astro # Was a Drupal footer block
BlogCard.astro # Was a View display mode
TeamMember.astro # Was a node--team-member.html.twig
layouts/
Base.astro # Was page.html.twig
BlogPost.astro # Was node--article.html.twig
public/
images/ # Files from sites/default/files/
documents/ # PDFs, downloads
astro.config.mjs # Build configuration
package.json # Dependencies (maybe 5-10, not hundreds)
Compare this to your Drupal installation:
- Drupal core: ~14,000 PHP files
- Contributed modules: 5,000-20,000 more PHP files
- Custom modules: Your actual custom code (hundreds of files at most)
- Theme: Twig templates, CSS, JavaScript
- Database: Hundreds of tables storing content, config, cache, sessions
- Composer vendor directory: Thousands more PHP dependency files
When you own the code, you can read every file in your project. You can understand every line. An AI coding agent can understand it too. There is no “Drupal magic” — no hook system, no render arrays, no cache tag invalidation, no entity access callbacks. Just files that generate HTML.
Understanding what you are leaving behind
Drupal’s complexity is not accidental. Every piece of that infrastructure serves a purpose:
- Entity/field system: Flexible content modeling without schema changes. You define content types in the UI and Drupal handles storage, validation, and rendering.
- Hook system: Extensibility without modifying core code. Modules can alter behavior at dozens of points.
- Views: A query builder that creates listings, feeds, blocks, and pages from content — without writing SQL.
- Permissions and roles: Granular access control. “Editor can edit their own articles but not pages. Manager can publish but not delete.”
- Config management: Export configuration to YAML, import on deployment, sync across environments.
- Module ecosystem: Thousands of contributed modules for SEO, media handling, forms, e-commerce, and more.
When you move to code, you trade all of this for simplicity. Content editing becomes editing files. Permissions become “who has Git access.” Configuration becomes code. And the module ecosystem becomes npm packages plus whatever you build yourself.
This is the right trade for many sites. But understand what you are giving up before you start.
Drupal’s content architecture: what needs extracting
Before you can convert to code, you need to get content out of Drupal. This is where Drupal is actually ahead of most CMSes — it has excellent content extraction options.
Drupal 8+ (JSON:API — the best option)
Drupal 8 and later ship with JSON:API as a core module. This gives you a full RESTful API for every entity type:
GET /jsonapi/node/article # All articles
GET /jsonapi/node/page # All pages
GET /jsonapi/taxonomy_term/tags # All tag terms
GET /jsonapi/media/image # All media entities
# With field relationships included
GET /jsonapi/node/article?include=field_image,field_category,uid
# Filtered to published content only
GET /jsonapi/node/article?filter[status]=1&page[limit]=50
JSON:API returns fully structured data with field values, relationships, and pagination. It is genuinely one of the best content APIs in the CMS world.
Drupal 7 (database export or contrib modules)
Drupal 7 does not have JSON:API. Your extraction options:
Direct database queries — the most reliable for D7:
-- Articles with body text and URL aliases
SELECT n.nid, n.title, n.created, n.changed,
b.body_value, b.body_summary,
ua.alias as url_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 (D7 stores each field in its own table)
SELECT entity_id, field_subtitle_value
FROM field_data_field_subtitle
WHERE entity_type = 'node' AND bundle = 'article';
-- Taxonomy terms for a vocabulary
SELECT td.tid, td.name, td.description
FROM taxonomy_term_data td
JOIN taxonomy_vocabulary tv ON td.vid = tv.vid
WHERE tv.machine_name = 'tags';
Views Data Export module — create a View for each content type, configure it to export as CSV or JSON.
Services module — install and configure to create REST endpoints similar to (but less elegant than) JSON:API.
Drush — drush sql-query for ad hoc database queries, or write a custom Drush command that exports content entities as JSON.
What to extract
For each content type, you need:
- Field values: Title, body, all custom fields (text, dates, numbers, links)
- Entity references: Which nodes reference other nodes, taxonomy terms, or users
- Media/files: File paths from
sites/default/files/(download these separately) - URL aliases: The clean URL for each node (for SEO preservation)
- Taxonomy: All vocabulary terms with their hierarchy
- Menus: Menu structure and links
Migration approaches: every option available
1. AI coding agents (Claude Code, Cursor, Windsurf, Cline)
This is the highest-leverage approach for developers. AI coding agents can consume Drupal’s APIs, extract all content, and scaffold a complete static site project.
The workflow with JSON:API (D8+):
- Confirm JSON:API is enabled: visit
https://your-site.com/jsonapi - Open your AI agent (Claude Code, Cursor, Windsurf, or Cline)
- Prompt: “Fetch all content from my Drupal site at example.com using JSON:API. List all content types available at /jsonapi. For each type, fetch all published nodes with their fields and relationships. Convert each node to a markdown file with frontmatter. Download all media files. Scaffold an Astro project with content collections matching each Drupal content type.”
- The agent iterates through API endpoints, creates files, downloads media
- Review, adjust, iterate until the project builds cleanly
The workflow with database export (D7):
- Export your Drupal database:
drush sql-dump > drupal7-export.sql(or from your hosting panel) - Run the SQL queries above to extract content as CSV or JSON
- Feed the exported data to your AI agent with instructions to convert it to markdown and scaffold a project
- The agent builds the project from the exported data
The workflow by crawling (any Drupal version):
If you cannot access the API or database, the agent can crawl the published site:
- “Crawl my website at example.com. Extract all page content, navigation, images, and structure. Rebuild it as a static site project.”
- The agent fetches pages, parses HTML, extracts content, downloads assets
- Less structured than API extraction, but works for any site
Real-world examples: Teams at companies like Prefect have documented using AI agents to rebuild sites. Sid Bharath has written about using Cursor to migrate content sites. The pattern works because Drupal’s content, once extracted, maps cleanly to file-based content.
2. AI app builders (Bolt.new, v0.dev, Lovable, Replit Agent)
For teams without a developer comfortable in the terminal:
- Export your Drupal content to JSON or CSV using Views Data Export or JSON:API
- Go to Bolt.new, v0.dev, Lovable, or Replit Agent
- Upload your content files or paste sample content
- Describe what you want: “Build a professional business website with a blog using this content”
- Iterate visually on the generated result
- Download the code or deploy directly
You can also screenshot your existing Drupal pages and use them as visual references. The builder will try to recreate the design with clean modern code.
This approach works best for simpler Drupal sites — 5-20 pages, a few content types, standard layouts. For complex Drupal sites with Paragraphs, many custom fields, and dozens of content types, a coding agent gives you more control.
3. Hire a Drupal migration specialist
The Drupal community has a well-established migration services market:
- Freelance Drupal developers: $2,000-$10,000 depending on site complexity. They understand Drupal’s entity system, Paragraphs module, and field storage schema intimately. Find them on Drupal.org’s marketplace, the Drupal Slack, or platforms like Toptal and Upwork.
- Drupal agencies: $10,000-$50,000 for enterprise Drupal sites with custom modules, complex content models, multilingual content, or integrations. Agencies like Lullabot, Acquia partners, and others offer dedicated migration services.
- Drupal developer hourly rates: $50-$200/hour. Specialized Drupal talent commands higher rates than general PHP or JavaScript development. This is worth considering when estimating DIY time — your own hours have value too.
Hiring a specialist makes sense when your Drupal site has complex patterns that automated tools handle poorly: deeply nested Paragraphs content, custom entity types, complex entity reference chains, multilingual content with the Entity Translation module, or custom modules with business logic that needs to be preserved or replaced.
4. BrowserCat Migrate (automated)
BrowserCat Migrate is an automated service that crawls your Drupal site, extracts content, and rebuilds it as a code project with a GitHub repo and live preview. It is one option among several for automated migration.
5. Drupal-native export tools
Drupal has strong content export capabilities built into the ecosystem:
- JSON:API (D8+ core): Full RESTful API for all entities. Best extraction option for D8+ sites.
- Views Data Export (contrib): Export any View as CSV, JSON, or XML. Flexible and configurable.
- Migrate module (D8+ core): Drupal’s own migration framework. Originally for D6/D7-to-D8 upgrades, but can export to custom destinations.
- Default Content module (contrib): Export entities as YAML files. Good for smaller sites.
- Drush: Command-line Swiss army knife. SQL dumps, entity exports, custom commands.
- Direct database access: For D7 sites especially, SQL queries against the field_data_* tables are the most reliable extraction method.
6. Manual migration (DIY)
- Audit your Drupal site: list every content type, custom field, taxonomy vocabulary, View, and block
- Choose your export method (JSON:API, database, Views Data Export)
- Write extraction scripts or queries for each content type
- Convert extracted content to markdown files with YAML frontmatter
- Download all files from
sites/default/files/ - Create a new project (Astro, Hugo, 11ty, or plain HTML)
- Build layouts and components matching your Drupal theme
- Implement listing pages to replace Drupal Views
- Set up redirects for any URL changes
- Deploy to a static host
The simplification in numbers
| Aspect | Drupal Installation | Code Project |
|---|---|---|
| Total files (with dependencies) | 30,000-70,000+ | 100-500 |
| Files you authored | 50-500 | 100-500 (all of them) |
| Languages | PHP, Twig, SQL, YAML, JS | JS/TS, HTML, CSS, Markdown |
| Database | Required (hundreds of tables) | None |
| Build/deploy steps | 5-8 (drush, config import, cache clear, etc.) | 1 (git push) |
| Security patches per year | 20-40 (core + contrib) | 0 (no server-side surface) |
| Hosting cost | $100-$500/mo (managed) | $0 (static hosting) |
| Annual maintenance cost | $3,600-$24,000 | Near-zero |
| Can an AI agent maintain it? | Partially (Drupal’s hook system is hard for AI) | Fully (standard file-based project) |
When to stay on Drupal instead
Drupal is genuinely excellent software. Converting to code is the wrong move if your site needs:
- Complex permissions: “This role can edit nodes in this section, but only during business hours, and only if they created the node.” Drupal’s permission system handles this. A static site cannot.
- Editorial workflows: Content staging, revision drafts, approval chains, scheduled publishing. Drupal has this built in. A file-based project needs external tooling (or a headless CMS) to replicate it.
- Multilingual content management: Drupal’s Content Translation module with translation workflows is enterprise-grade. Mapping this to file-based i18n is possible but loses the management UI.
- Custom entity relationships: If your content model relies heavily on entity references — events referencing venues, programs referencing courses, courses referencing instructors — and these relationships drive application behavior, Drupal’s entity system is hard to replace.
- Enterprise integrations: SSO, LDAP, CRM integrations via Drupal modules.
The Drupal community is tight-knit and opinionated for good reason — they have built something genuinely powerful. The migration to code is not a judgment on Drupal’s quality. It is a recognition that many sites do not use what Drupal provides and are paying for complexity they do not need.
The Drupal 7 situation
Drupal 7 reached end of life in January 2025. If you are on D7, your options are:
- Upgrade to Drupal 10: The official path. Requires rebuilding custom modules, converting themes from PHPTemplate to Twig, migrating database schemas, and testing all contributed module replacements. Typically 3-6 months and $50,000-$200,000 for a mid-size site.
- Extended D7 support: Third-party vendors offer paid security patches. This delays the migration but adds ongoing cost without solving the underlying problem.
- Migrate to code: Extract content, build a modern static project. Days to weeks depending on approach and site complexity.
For D7 sites that are content sites, option 3 is often the most cost-effective. But if your D7 site genuinely uses Drupal’s advanced features and you have the budget, D10 is excellent software and the upgrade is a legitimate choice.
The Drupal to WordPress intermediate step
Some organizations consider Drupal to WordPress as an intermediate step, especially if they want to stay on a CMS with a browser-based editing experience. WordPress has a lower cost of ownership than Drupal for content sites, a vastly larger developer pool, and more accessible hosting options.
This is a valid approach, but it trades one CMS for another. You still have a database, PHP, security patches, and hosting costs — just less of each. If your end goal is simplicity and low maintenance, going directly to code skips the intermediate step.
After the migration
Once your content lives in a code project:
- Content updates are file edits. Change a markdown file, commit, push, and the site rebuilds in seconds.
- Design changes are component edits. Modify an
.astroor.htmlfile and see the result immediately. - Any developer can maintain it. No Drupal expertise needed. No PHP knowledge needed. Any JavaScript developer — or an AI coding agent — can read and modify every file.
- If you want a CMS editing UI, add a headless CMS like Decap CMS (free, Git-backed), Tina CMS, or Sanity. These provide browser-based editing on top of file-based content, giving non-technical editors a familiar interface without Drupal’s infrastructure overhead.
The fundamental shift is from administering an application (Drupal) to owning a project (your code). Both are valid approaches to running a website. But for content sites, ownership is simpler.
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…