Headless Drupal: The Complete Guide for 2026
Headless Drupal separates Drupal's content management backend from the frontend that renders it. Instead of using Drupal's built-in theming layer (Twig templates, PHP rendering), you expose content through APIs and build your frontend with whatever technology you want — React, Next.js, Vue, a mobile app, or an AI agent.
This isn't a new idea. Developers have been using Drupal as a headless CMS since at least 2015. What's changed is that the tooling has matured, the APIs are now built into Drupal core, and managed platforms have eliminated the operational pain that made headless Drupal impractical for smaller teams.
This guide covers the architecture, the APIs, practical integration code, and the trade-offs you should understand before committing to headless Drupal in 2026.
What Is Headless Drupal?
In a traditional Drupal site, the CMS handles everything: content storage, business logic, URL routing, HTML rendering, and page delivery. The browser requests a URL, Drupal processes it, and returns a fully rendered HTML page.
In a headless Drupal setup, Drupal only handles content storage and API delivery. A separate frontend application — typically a JavaScript framework like React or Next.js — fetches content from Drupal's API and handles all rendering, routing, and user interaction.
The term "decoupled Drupal" is often used interchangeably with "headless Drupal," though some draw a distinction: fully decoupled means the frontend is completely separate, while progressively decoupled means Drupal still renders the page shell but JavaScript frameworks handle interactive regions. This guide focuses on the fully decoupled approach, which is where the ecosystem has converged. For more on this architecture and its implications, see our article on decoupled Drupal.
Why Drupal Is Uniquely Suited for Headless
There are dozens of headless CMS platforms in 2026. Drupal's advantage isn't that it's the simplest option — it's that it brings capabilities that purpose-built headless platforms are still trying to replicate.
20+ years of content modeling. Drupal's entity-field system is one of the most sophisticated content modeling tools available. Content types with typed fields, entity references for relational data, Paragraphs for component-based content authoring, and taxonomy vocabularies for classification. This is the kind of structured data modeling that takes months to build from scratch and that many SaaS headless platforms still handle clumsily.
Built-in JSON:API. Since Drupal 8, JSON:API has been included in core. Every content type, taxonomy, media entity, and user you create is automatically available through a standards-compliant JSON:API endpoint. No configuration, no custom code. You create a content type, and the API endpoint exists immediately.
GraphQL via contributed module. The GraphQL Compose module provides a generated GraphQL schema that mirrors your content model. It supports queries, filtering, sorting, and pagination out of the box. Unlike hand-rolled GraphQL APIs, the schema updates automatically when you change your content model.
Multilingual from the core. Drupal's multilingual system is baked into the entity system. Content translations, interface translations, configuration translations, and language negotiation are all core features. Contentful and Sanity offer localization, but Drupal's implementation is deeper — each translation is a full entity revision with its own moderation state.
Content moderation and editorial workflows. Draft, review, published, archived — Drupal's content moderation system lets you define custom editorial workflows with role-based transitions. Your API can serve published content to production and draft content to preview environments, all from the same Drupal instance.
Open-source data ownership. Your content lives in a database you control. There's no vendor lock-in on the data layer, no API call limits that throttle your application, and no surprise pricing changes that force a migration.
Headless Drupal Architecture
The architecture of a headless Drupal application has three layers:
┌─────────────────────────────────────────────┐
│ Frontend Layer │
│ (Next.js / React / Vue / Mobile App) │
│ │
│ - Fetches content from API │
│ - Handles routing, rendering, interaction │
│ - Deployed to Vercel, Netlify, etc. │
└──────────────────┬──────────────────────────┘
│ JSON:API or GraphQL
▼
┌─────────────────────────────────────────────┐
│ API Layer │
│ (Drupal JSON:API / GraphQL) │
│ │
│ - Auto-generated from content model │
│ - Filtering, sorting, pagination │
│ - Authentication and access control │
└──────────────────┬──────────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ Content Layer │
│ (Drupal Backend) │
│ │
│ - Content types, fields, media │
│ - Editorial workflows │
│ - User roles and permissions │
└─────────────────────────────────────────────┘
The frontend and backend are deployed independently. The API contract between them is the only coupling point.
JSON:API vs GraphQL
Drupal offers two API approaches, and you don't have to choose one — many projects use both. For a deeper dive, see the JSON:API docs and GraphQL docs.
JSON:API is included in Drupal core. It follows the JSON:API specification (jsonapi.org), which means predictable URL patterns, standardized filtering, and well-defined relationship handling. It's RESTful and works well with simple fetch calls.
GraphQL (via GraphQL Compose) gives you a single endpoint and the ability to request exactly the fields you need. It reduces over-fetching and is particularly useful when your frontend has complex data requirements across multiple content types.
| JSON:API | GraphQL | |
|---|---|---|
| Included in core | Yes | No (contributed module) |
| Query flexibility | Filter/sort/include params | Full query language |
| Over-fetching | Controlled via fields param |
Minimal by design |
| Caching | HTTP caching friendly | Requires client-side caching |
| Learning curve | Low (REST conventions) | Medium (query language) |
| Best for | Simple CRUD, mobile apps | Complex UIs, multiple content types per page |
Fetching Content from Drupal's JSON:API
Here's how to fetch a list of articles from a Drupal JSON:API endpoint in a Next.js application:
// lib/drupal.ts
const DRUPAL_BASE_URL = process.env.DRUPAL_BASE_URL
interface DrupalArticle {
id: string
attributes: {
title: string
body: { processed: string }
created: string
path: { alias: string }
}
relationships: {
field_image: { data: { id: string } }
field_category: { data: { id: string } }
}
}
export async function getArticles(): Promise<DrupalArticle[]> {
const url = new URL('/jsonapi/node/article', DRUPAL_BASE_URL)
// Filter to published content, include related entities
url.searchParams.set('filter[status]', '1')
url.searchParams.set('include', 'field_image,field_category')
url.searchParams.set('sort', '-created')
url.searchParams.set('page[limit]', '10')
const res = await fetch(url.toString(), {
headers: { 'Accept': 'application/vnd.api+json' },
next: { revalidate: 60 }, // ISR: revalidate every 60 seconds
})
if (!res.ok) throw new Error(`Failed to fetch articles: ${res.status}`)
const json = await res.json()
return json.data
}
The include parameter is important — it tells Drupal to embed related entities (images, categories) in the response, avoiding the N+1 problem of separate requests for each relationship.
Fetching Content with GraphQL
The same query using GraphQL Compose:
query GetArticles {
nodeArticles(first: 10, sortKey: CREATED_AT) {
nodes {
id
title
body {
processed
}
path
created {
timestamp
}
fieldImage {
url
alt
}
fieldCategory {
name
}
}
}
}
And the corresponding fetch in your Next.js application:
// lib/drupal-graphql.ts
const DRUPAL_GRAPHQL_URL = `${process.env.DRUPAL_BASE_URL}/graphql`
export async function getArticlesGraphQL() {
const query = `
query GetArticles {
nodeArticles(first: 10, sortKey: CREATED_AT) {
nodes {
id
title
body { processed }
path
created { timestamp }
fieldImage { url alt }
fieldCategory { name }
}
}
}
`
const res = await fetch(DRUPAL_GRAPHQL_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query }),
next: { revalidate: 60 },
})
const json = await res.json()
return json.data.nodeArticles.nodes
}
Notice how the GraphQL response shape matches exactly what you requested — no extraneous fields, no nested attributes and relationships wrappers. This is why teams with complex frontend data requirements often prefer GraphQL.
Headless Drupal with React
React is the most common frontend choice for headless Drupal projects. A basic setup involves:
- A Drupal backend exposing content via JSON:API or GraphQL
- A React application (Create React App, Vite, or a meta-framework) fetching from those APIs
- State management for content data (React Query / TanStack Query is the most common choice)
// components/ArticleList.tsx
import { useQuery } from '@tanstack/react-query'
function ArticleList() {
const { data: articles, isLoading } = useQuery({
queryKey: ['articles'],
queryFn: () =>
fetch(`${process.env.REACT_APP_DRUPAL_URL}/jsonapi/node/article?sort=-created`)
.then(res => res.json())
.then(json => json.data),
})
if (isLoading) return <div>Loading...</div>
return (
<ul>
{articles?.map((article: any) => (
<li key={article.id}>
<h2>{article.attributes.title}</h2>
<div dangerouslySetInnerHTML={{
__html: article.attributes.body.processed
}} />
</li>
))}
</ul>
)
}
This works, but it's client-rendered — the browser fetches content after the page loads. For SEO-critical content, you'll want server-side rendering, which leads most teams to Next.js.
Headless Drupal with Next.js
Next.js is the strongest pairing for headless Drupal in 2026. Its App Router with React Server Components means you can fetch Drupal content on the server, render it to HTML, and send a fully rendered page to the browser — no client-side loading states, no SEO compromises.
// app/articles/page.tsx (Next.js App Router)
import { getArticles } from '@/lib/drupal'
export const revalidate = 60 // ISR: rebuild every 60 seconds
export default async function ArticlesPage() {
const articles = await getArticles()
return (
<main>
<h1>Articles</h1>
{articles.map((article) => (
<article key={article.id}>
<h2>{article.attributes.title}</h2>
<div dangerouslySetInnerHTML={{
__html: article.attributes.body.processed
}} />
</article>
))}
</main>
)
}
Key advantages of Next.js + Drupal:
- Incremental Static Regeneration (ISR) lets you cache pages at the edge and revalidate them on a schedule or on-demand via webhooks.
- Server Components fetch data on the server without shipping the fetch logic to the browser, reducing bundle size.
- Draft mode lets editors preview unpublished Drupal content by toggling a cookie — your Next.js app fetches from Drupal's draft endpoint instead of the published one.
- Image optimization via
next/imageworks with Drupal's media system — just point it at the image URLs from your API responses.
For a detailed comparison of CMS options for Next.js projects, see Best CMS for Next.js in 2026.
Headless Drupal with Vue
Vue (and Nuxt as its meta-framework) is another solid choice for headless Drupal frontends. The fetching patterns are similar:
<!-- pages/articles.vue (Nuxt 3) -->
<script setup>
const { data: articles } = await useFetch(
`${useRuntimeConfig().public.drupalUrl}/jsonapi/node/article`,
{
params: { 'sort': '-created', 'filter[status]': '1' },
transform: (response) => response.data,
}
)
</script>
<template>
<main>
<h1>Articles</h1>
<article v-for="article in articles" :key="article.id">
<h2>{{ article.attributes.title }}</h2>
<div v-html="article.attributes.body.processed" />
</article>
</main>
</template>
Nuxt provides the same server-rendering and static generation capabilities as Next.js. The choice between them is usually a matter of team preference — React vs Vue — rather than any technical limitation on the Drupal side. Drupal's APIs are framework-agnostic by nature.
The Two-Stack Problem
Here's the part that most headless Drupal tutorials skip: running this architecture in production is hard.
Drupal requires PHP, a web server (Apache or Nginx), a MySQL or PostgreSQL database, and ideally a Redis or Memcached instance for caching. Your Next.js frontend requires Node.js and a deployment target like Vercel or Netlify. That's two runtime environments, two deployment pipelines, two sets of dependencies to maintain, and two different skill sets on your team.
This is the two-stack problem, and it's the primary reason that headless Drupal adoption has been slower than the technology deserves. The CMS is powerful, the APIs are solid, but the operational overhead of running and maintaining a Drupal instance alongside a JavaScript frontend is a genuine barrier.
What the two-stack problem looks like in practice:
- A PHP security patch requires updating your Drupal server on a Thursday, while your frontend team is mid-sprint on a Next.js feature
- A new developer joins the team and spends a full day setting up Docker Compose with MySQL, PHP-FPM, Redis, and a Node.js dev server
- A Drupal module update changes API output, breaking your frontend — but you don't discover it until after deploying the Drupal update
- Your frontend team has no PHP experience and can't debug a Drupal issue without pulling in a backend developer
How Managed Platforms Solve This
This is the problem that Decoupled.io was built to solve. Instead of running your own Drupal infrastructure, you get a managed Drupal backend that's provisioned automatically, maintained continuously, and accessible entirely through APIs and AI-powered tools.
What Decoupled.io handles for you:
- Infrastructure: Drupal instances are provisioned with a single API call. No servers to configure, no Docker Compose files, no PHP runtime to manage.
- Updates and security: Core updates, security patches, and module maintenance are handled by the platform. You never run
composer updateor worry about a Drupal SA (security advisory). - API delivery: Both JSON:API and GraphQL are available out of the box, fully configured and optimized.
- Content modeling: Create content types, fields, and relationships through the admin panel or through MCP tools in your AI assistant. The API schema updates immediately.
- AI-native workflow: Use MCP tools to manage content, create content types, import data, and generate frontend integration code — all from your coding environment.
The result is that your team only manages one stack: the frontend. Drupal's content modeling power is fully available, but the PHP ecosystem, the server infrastructure, and the update lifecycle are invisible to your developers.
Self-Hosted vs Decoupled.io vs Acquia
If you've decided on headless Drupal, you have three realistic deployment options in 2026:
Self-Hosted Drupal
You manage everything: servers (or containers), the Drupal codebase, contributed modules, database backups, security patches, and performance tuning.
Pros:
- Full control over every aspect of the Drupal installation
- Install any contributed module or custom code
- No vendor dependency beyond Drupal itself (open source)
- Can be cost-effective if you already have the DevOps expertise
Cons:
- Requires PHP and Drupal expertise on staff
- Ongoing maintenance burden (updates, security, scaling)
- You own the two-stack problem entirely
- Time-to-first-content is measured in days or weeks, not minutes
Best for: Teams with existing Drupal and DevOps expertise who need deep customization (custom modules, complex access control, integration with legacy PHP systems).
Decoupled.io (Managed Headless Drupal)
Drupal infrastructure is fully managed. You interact with the CMS through APIs and AI tools.
Pros:
- Zero PHP or Drupal infrastructure knowledge required
- Instant provisioning — a new content backend in minutes
- Curated module set that covers the common 80% of use cases
- AI-native management through MCP tools
- Both JSON:API and GraphQL included
- Open-source Drupal under the hood — no proprietary data format
Cons:
- Cannot install arbitrary Drupal modules
- No SSH access to the Drupal server
- Less suitable for projects that need deep Drupal customization
Best for: Frontend teams, AI app builders, and agencies that want Drupal's content modeling without managing Drupal infrastructure. Get started here.
Acquia (Enterprise Drupal Hosting)
Acquia is the enterprise Drupal hosting platform founded by Drupal's creator. It provides managed Drupal hosting with a full suite of enterprise tools.
Pros:
- Full Drupal flexibility (custom modules, themes, hooks)
- Enterprise features: CDN, personalization, DAM, marketing tools
- Strong compliance and security certifications
- Drupal expertise baked into the platform
Cons:
- Enterprise pricing ($30K-$200K+/year for most configurations)
- Still requires Drupal development expertise on your team
- Doesn't eliminate the two-stack problem — you still manage PHP and Node.js
- Heavyweight for teams that just need a content API
Best for: Large enterprises with existing Drupal teams, compliance requirements, and budgets that match.
When Headless Drupal Is the Right Choice
Headless Drupal makes sense when you need:
- Complex content modeling — entity references, Paragraphs, taxonomy hierarchies, and typed fields that go beyond what flat-content CMS platforms offer
- Multilingual content — Drupal's translation system is more mature than any SaaS headless CMS
- Editorial workflows — content moderation with custom states and role-based transitions
- Data ownership — your content in an open-source system with no vendor lock-in
- Multiple frontends — the same content powering a website, mobile app, and AI agent
It's probably not the right choice if you need a CMS running in 10 minutes with no learning curve. Flat-content platforms like Contentful or Sanity are simpler to start with, even if they hit limits faster as complexity grows. See our headless CMS comparison for a detailed breakdown.
Getting Started
If you want to try headless Drupal without committing to infrastructure:
- Create a free Decoupled.io space — no PHP, no servers, no credit card
- Build a content model — create content types and fields through the admin panel or MCP tools
- Query the API — use the JSON:API or GraphQL endpoints from your frontend
- Connect your framework — use the code examples above for Next.js, React, or Vue
You'll have a working headless Drupal backend serving content to your frontend in under 10 minutes. The Drupal expertise is optional. The content modeling power is not.
Ready to build? Get started with Decoupled.io and connect your Next.js or React frontend to a managed Drupal backend — no PHP required.